Unstable 0.17.6.0

This commit is contained in:
Markus Isberg
2022-04-04 16:46:08 +09:00
parent 44ded0225a
commit 95764d1fa8
78 changed files with 1265 additions and 703 deletions
@@ -3450,7 +3450,7 @@ namespace Barotrauma
{
ChangeParams("wall", state, priority / 2);
}
if (canAttackDoors)
if (canAttackDoors && IsAggressiveBoarder)
{
ChangeParams("door", state, priority / 2);
}
@@ -376,19 +376,17 @@ namespace Barotrauma
{
Vector2 diff = currentPath.CurrentNode.WorldPosition - pos;
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
if (nextLadderSameAsCurrent)
if (nextLadderSameAsCurrent || currentLadder != null && nextLadder != null && Math.Abs(currentLadder.Item.Position.X - nextLadder.Item.Position.X) < 50)
{
//climbing ladders -> don't move horizontally
diff.X = 0.0f;
}
//at the same height as the waypoint
if (Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < (collider.height / 2 + collider.radius) * 1.25f)
float heightDiff = Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y);
float colliderSize = (collider.height / 2 + collider.radius) * 1.25f;
if (heightDiff < colliderSize)
{
float heightFromFloor = character.AnimController.GetHeightFromFloor();
if (heightFromFloor <= 0.0f)
{
diff.Y = Math.Max(diff.Y, 100);
}
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
bool isAboveFloor = heightFromFloor > -0.1f;
// If the next waypoint is horizontally far, we don't want to keep holding the ladders
@@ -402,7 +400,10 @@ namespace Barotrauma
// Try to change the ladder (hatches between two submarines)
if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition))
{
nextLadder.Item.TryInteract(character, forceSelectKey: true);
if (nextLadder.Item.TryInteract(character, forceSelectKey: true))
{
NextNode(!doorsChecked);
}
}
}
if (isAboveFloor || nextLadderSameAsCurrent)
@@ -461,12 +462,16 @@ namespace Barotrauma
bool isTargetTooLow = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y;
var door = currentPath.CurrentNode.ConnectedDoor;
float margin = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 5, 0, 1));
if (currentPath.CurrentNode.Stairs != null && currentPath.NextNode?.Stairs == null)
if (currentPath.CurrentNode.Stairs != null)
{
margin = 1;
if (currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + character.AnimController.ColliderHeightFromFloor * 0.25f)
bool isNextNodeInSameStairs = currentPath.NextNode?.Stairs == currentPath.CurrentNode.Stairs;
if (!isNextNodeInSameStairs)
{
isTargetTooLow = true;
margin = 1;
if (currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + character.AnimController.ColliderHeightFromFloor * 0.25f)
{
isTargetTooLow = true;
}
}
}
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
@@ -630,7 +630,7 @@ namespace Barotrauma
public bool IsActiveObjective<T>() where T : AIObjective => GetActiveObjective() is T;
public AIObjective GetActiveObjective() => CurrentObjective?.GetActiveObjective();
public T GetOrder<T>() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T).Objective as T;
public T GetOrder<T>() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T)?.Objective as T;
/// <summary>
/// Returns the last active objective of the specific type.
@@ -24,6 +24,8 @@ namespace Barotrauma
public bool IsAiming => wasAiming;
public bool IsAimingMelee => wasAimingMelee;
protected bool Aiming => aiming || aimingMelee;
public float ArmLength => upperArmLength + forearmLength;
public abstract GroundedMovementParams WalkParams { get; set; }
@@ -193,7 +193,7 @@ namespace Barotrauma
strongestImpact = 0.0f;
}
if (aiming)
if (Aiming)
{
TargetMovement = TargetMovement.ClampLength(2);
}
@@ -233,7 +233,7 @@ namespace Barotrauma
//don't flip when simply physics is enabled
if (SimplePhysicsEnabled) { return; }
if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip) && !aiming)
if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip) && !Aiming)
{
if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
{
@@ -597,11 +597,11 @@ namespace Barotrauma
{
float torsoAngle = TorsoAngle.Value;
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
if (Crouching && !movingHorizontally && !aiming) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; }
if (Crouching && !movingHorizontally && !Aiming) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; }
torsoAngle -= herpesStrength / 150.0f;
torso.body.SmoothRotate(torsoAngle * Dir, CurrentGroundedParams.TorsoTorque);
}
if (!aiming && CurrentGroundedParams.FixedHeadAngle && HeadAngle.HasValue)
if (!Aiming && CurrentGroundedParams.FixedHeadAngle && HeadAngle.HasValue)
{
float headAngle = HeadAngle.Value;
if (Crouching && !movingHorizontally) { headAngle -= HumanCrouchParams.ExtraHeadAngleWhenStationary; }
@@ -817,48 +817,16 @@ namespace Barotrauma
Limb torso = GetLimb(LimbType.Torso);
if (head == null) { return; }
if (torso == null) { return; }
//check both hulls: the hull whose coordinate space the ragdoll is in, and the hull whose bounds the character's origin actually is inside
const float DisableMovementAboveSurfaceThreshold = 50.0f;
if (currentHull != null && character.CurrentHull != null)
{
float surfacePos = currentHull.Surface;
float surfacePos = GetSurfaceY();
float surfaceThreshold = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f);
//if the hull is almost full of water, check if there's a water-filled hull above it
//and use its water surface instead of the current hull's
if (currentHull.Rect.Y - currentHull.Surface < 5.0f)
{
GetSurfacePos(currentHull, ref surfacePos);
void GetSurfacePos(Hull hull, ref float prevSurfacePos)
{
if (prevSurfacePos > surfaceThreshold) { return; }
foreach (Gap gap in hull.ConnectedGaps)
{
if (gap.IsHorizontal || gap.Open <= 0.0f || gap.WorldPosition.Y < hull.WorldPosition.Y) { continue; }
if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; }
//if the gap is above us and leads outside, there's no surface to limit the movement
if (!gap.IsRoomToRoom && gap.Position.Y > hull.Position.Y)
{
prevSurfacePos += 100000.0f;
return;
}
foreach (var linkedTo in gap.linkedTo)
{
if (linkedTo is Hull otherHull && otherHull != hull && otherHull != currentHull)
{
prevSurfacePos = Math.Max(surfacePos, otherHull.Surface);
GetSurfacePos(otherHull, ref prevSurfacePos);
break;
}
}
}
}
}
surfaceLimiter = Math.Max(1.0f, surfaceThreshold - surfacePos);
if (surfaceLimiter > 50.0f) { return; }
}
if (surfaceLimiter > DisableMovementAboveSurfaceThreshold) { return; }
}
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
@@ -872,25 +840,30 @@ namespace Barotrauma
{
rotation += 360;
}
if (!character.IsRemotelyControlled && !aiming && Anim != Animation.UsingConstruction &&
!(character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false))
float targetSpeed = TargetMovement.Length();
if (targetSpeed > 0.1f && !character.IsRemotelyControlled && !character.IsKeyDown(InputType.Aim))
{
if (rotation > 20 && rotation < 170)
if (Anim != Animation.UsingConstruction && !(character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false))
{
TargetDir = Direction.Left;
}
else if (rotation > 190 && rotation < 340)
{
TargetDir = Direction.Right;
if (rotation > 20 && rotation < 170)
{
TargetDir = Direction.Left;
}
else if (rotation > 190 && rotation < 340)
{
TargetDir = Direction.Right;
}
}
}
float targetSpeed = TargetMovement.Length();
if (aiming)
if (Aiming)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 diff = (mousePos - torso.SimPosition) * Dir;
float newRotation = MathUtils.VectorToAngle(diff);
Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
if (diff.LengthSquared() > MathUtils.Pow2(0.4f))
{
float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 * Dir);
Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
}
}
else if (targetSpeed > 0.1f)
{
@@ -911,7 +884,7 @@ namespace Barotrauma
torso.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.TorsoTorque);
}
if (!aiming && CurrentSwimParams.FixedHeadAngle && HeadAngle.HasValue)
if (!Aiming && CurrentSwimParams.FixedHeadAngle && HeadAngle.HasValue)
{
head.body.SmoothRotate(Collider.Rotation + HeadAngle.Value * Dir, CurrentSwimParams.HeadTorque);
}
@@ -940,7 +913,7 @@ namespace Barotrauma
head.body.ApplyTorque(Dir);
}
movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / 50.0f));
movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / DisableMovementAboveSurfaceThreshold));
}
bool isNotRemote = true;
@@ -1141,10 +1114,9 @@ namespace Barotrauma
bottomPos + torsoPos + movement.Y * 0.1f - ladderSimPos.Y);
if (climbFast) { handPos.Y -= stepHeight; }
bool aiming = this.aiming || aimingMelee;
//prevent the hands from going above the top of the ladders
handPos.Y = Math.Min(-0.5f, handPos.Y);
if (!aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
MoveLimb(rightHand,
new Vector2(slide ? handPos.X + ladderSimSize.X * 0.5f : handPos.X,
@@ -1152,7 +1124,7 @@ namespace Barotrauma
5.2f);
rightHand.body.ApplyTorque(Dir * 2.0f);
}
if (!aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
MoveLimb(leftHand,
new Vector2(handPos.X - ladderSimSize.X * 0.5f,
@@ -1235,7 +1207,7 @@ namespace Barotrauma
//apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
if (aiming)
if (Aiming)
{
RotateHead(head);
}
@@ -1526,11 +1498,14 @@ namespace Barotrauma
return;
}
Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso);
if (targetTorso == null) targetTorso = target.AnimController.MainLimb;
if (targetTorso == null)
{
targetTorso = target.AnimController.MainLimb;
}
if (target.AnimController.Dir != Dir)
{
target.AnimController.Flip();
}
Vector2 transformedTorsoPos = torso.SimPosition;
if (character.Submarine == null && target.Submarine != null)
{
@@ -1574,7 +1549,10 @@ namespace Barotrauma
{
//only grab with one hand when swimming
leftHand.Disabled = true;
if (!inWater) rightHand.Disabled = true;
if (!inWater)
{
rightHand.Disabled = true;
}
for (int i = 0; i < 2; i++)
{
@@ -1193,13 +1193,9 @@ namespace Barotrauma
headInWater = false;
inWater = false;
RefreshFloorY(ignoreStairs: Stairs == null);
if (currentHull.WaterVolume > currentHull.Volume * 0.95f)
if (currentHull.WaterPercentage > 0.001f)
{
inWater = true;
}
else
{
float waterSurface = ConvertUnits.ToSimUnits(currentHull.Surface);
float waterSurface = ConvertUnits.ToSimUnits(GetSurfaceY());
if (targetMovement.Y < 0.0f)
{
Vector2 colliderBottom = GetColliderBottom();
@@ -1212,11 +1208,8 @@ namespace Barotrauma
if (lowerHull != null) floorY = ConvertUnits.ToSimUnits(lowerHull.Rect.Y - lowerHull.Rect.Height);
}
}
float standHeight =
HeadPosition.HasValue ? HeadPosition.Value :
TorsoPosition.HasValue ? TorsoPosition.Value :
Collider.GetMaxExtent() * 0.5f;
if (Collider.SimPosition.Y < waterSurface && waterSurface - floorY > standHeight * 0.95f)
float standHeight = HeadPosition ?? TorsoPosition ?? Collider.GetMaxExtent() * 0.5f;
if (Collider.SimPosition.Y < waterSurface && waterSurface - floorY > standHeight * 0.8f)
{
inWater = true;
}
@@ -1521,7 +1514,6 @@ namespace Barotrauma
}
}
private float GetFloorY(Vector2 simPosition, bool ignoreStairs = false)
{
onGround = false;
@@ -1640,6 +1632,51 @@ namespace Barotrauma
}
}
public float GetSurfaceY()
{
//check both hulls: the hull whose coordinate space the ragdoll is in, and the hull whose bounds the character's origin actually is inside
if (currentHull == null || character.CurrentHull == null)
{
return float.PositiveInfinity;
}
float surfacePos = currentHull.Surface;
float surfaceThreshold = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f);
//if the hull is almost full of water, check if there's a water-filled hull above it
//and use its water surface instead of the current hull's
if (currentHull.Rect.Y - currentHull.Surface < 5.0f)
{
GetSurfacePos(currentHull, ref surfacePos);
void GetSurfacePos(Hull hull, ref float prevSurfacePos)
{
if (prevSurfacePos > surfaceThreshold) { return; }
foreach (Gap gap in hull.ConnectedGaps)
{
if (gap.IsHorizontal || gap.Open <= 0.0f || gap.WorldPosition.Y < hull.WorldPosition.Y) { continue; }
if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; }
//if the gap is above us and leads outside, there's no surface to limit the movement
if (!gap.IsRoomToRoom && gap.Position.Y > hull.Position.Y)
{
prevSurfacePos += 100000.0f;
return;
}
foreach (var linkedTo in gap.linkedTo)
{
if (linkedTo is Hull otherHull && otherHull != hull && otherHull != currentHull)
{
prevSurfacePos = Math.Max(surfacePos, otherHull.Surface);
GetSurfacePos(otherHull, ref prevSurfacePos);
break;
}
}
}
}
}
return surfacePos;
}
public void SetPosition(Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true, bool forceMainLimbToCollider = false, bool detachProjectiles = true)
{
if (!MathUtils.IsValid(simPosition))
@@ -349,7 +349,6 @@ namespace Barotrauma
DamageRange = range;
StructureDamage = LevelWallDamage = structureDamage;
ItemDamage = itemDamage;
Penetration = Penetration;
}
public Attack(ContentXElement element, string parentDebugName, Item sourceItem) : this(element, parentDebugName)
@@ -359,7 +358,7 @@ namespace Barotrauma
public Attack(ContentXElement element, string parentDebugName)
{
Deserialize(element);
Deserialize(element, parentDebugName);
if (element.GetAttribute("damage") != null ||
element.GetAttribute("bluntdamage") != null ||
@@ -423,7 +422,7 @@ namespace Barotrauma
}
partial void InitProjSpecific(ContentXElement element);
public void ReloadAfflictions(XElement element)
public void ReloadAfflictions(XElement element, string parentDebugName)
{
Afflictions.Clear();
foreach (var subElement in element.GetChildElements("affliction"))
@@ -431,6 +430,11 @@ namespace Barotrauma
AfflictionPrefab afflictionPrefab;
Affliction affliction;
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier))
{
DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\".");
continue;
}
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
affliction = afflictionPrefab.Instantiate(0.0f);
affliction.Deserialize(subElement);
@@ -456,10 +460,10 @@ namespace Barotrauma
}
}
public void Deserialize(XElement element)
public void Deserialize(XElement element, string parentDebugName)
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ReloadAfflictions(element);
ReloadAfflictions(element, parentDebugName);
}
public AttackResult DoDamage(Character attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true, PhysicsBody sourceBody = null, Limb sourceLimb = null)
@@ -556,6 +556,12 @@ namespace Barotrauma
#if CLIENT
CharacterHealth.SetHealthBarVisibility(value == null);
#elif SERVER
if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet })
{
Wallet.Give(balance);
grabbedWallet.Deduct(balance);
}
#endif
}
}
@@ -1180,7 +1186,7 @@ namespace Barotrauma
CharacterHealth = new CharacterHealth(selectedHealthElement, this, limbHealthElement);
}
if (Params.Husk)
if (Params.Husk && speciesName != "husk")
{
// Get the non husked name and find the ragdoll with it
var matchingAffliction = AfflictionPrefab.List
@@ -1764,26 +1770,44 @@ namespace Barotrauma
}
if (!aiControlled &&
AnimController.OnGround &&
!AnimController.InWater &&
AnimController.Anim != AnimController.Animation.UsingConstruction &&
AnimController.Anim != AnimController.Animation.CPR &&
(GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient || Controlled == this))
(GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient || Controlled == this) &&
(AnimController.OnGround && !AnimController.InWater || IsKeyDown(InputType.Aim) && HeldItems.None(i => i.RequireAimToUse)))
{
//Limb head = AnimController.GetLimb(LimbType.Head);
// Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping.
float followMargin = 40;
if (dontFollowCursor)
{
AnimController.TargetDir = Direction.Right;
}
else if (cursorPosition.X < AnimController.Collider.Position.X - followMargin)
else
{
AnimController.TargetDir = Direction.Left;
}
else if (cursorPosition.X > AnimController.Collider.Position.X + followMargin)
{
AnimController.TargetDir = Direction.Right;
// Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping.
float followMargin = 40;
Vector2 diff = CursorPosition - AnimController.Collider.Position;
if (InWater)
{
followMargin = 80;
diff = Vector2.Transform(diff, Matrix.CreateRotationZ(-AnimController.Collider.Rotation));
if (diff.X < followMargin)
{
AnimController.TargetDir = Direction.Left;
}
else if (diff.X > followMargin)
{
AnimController.TargetDir = Direction.Right;
}
}
else
{
if (CursorPosition.X < AnimController.Collider.Position.X - followMargin)
{
AnimController.TargetDir = Direction.Left;
}
else if (CursorPosition.X > AnimController.Collider.Position.X + followMargin)
{
AnimController.TargetDir = Direction.Right;
}
}
}
}
@@ -53,16 +53,18 @@ namespace Barotrauma
continue;
}
var vitalityMultipliers = subElement.GetAttributeIdentifierArray("identifier", null) ?? subElement.GetAttributeIdentifierArray("identifiers", null);
if (vitalityMultipliers == null)
{
vitalityMultipliers = subElement.GetAttributeIdentifierArray("type", null) ?? subElement.GetAttributeIdentifierArray("types", null);
}
if (vitalityMultipliers != null)
{
float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f);
vitalityMultipliers.ForEach(i => VitalityMultipliers.Add(i, multiplier));
}
else
var vitalityTypeMultipliers = subElement.GetAttributeIdentifierArray("type", null) ?? subElement.GetAttributeIdentifierArray("types", null);
if (vitalityTypeMultipliers != null)
{
float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f);
vitalityTypeMultipliers.ForEach(i => VitalityTypeMultipliers.Add(i, multiplier));
}
if (vitalityMultipliers == null && VitalityTypeMultipliers == null)
{
DebugConsole.ThrowError($"Error in character health config {characterHealth.Character.Name}: affliction identifier(s) or type(s) not defined in the \"VitalityMultiplier\" elements!");
}
@@ -1116,14 +1116,13 @@ namespace Barotrauma
public AttackParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
{
var prefab = CharacterPrefab.Prefabs[ragdoll.SpeciesName];
Attack = new Attack(element, ragdoll.SpeciesName.Value);
}
public override bool Deserialize(XElement element = null, bool recursive = true)
{
base.Deserialize(element, recursive);
Attack.Deserialize(element ?? Element);
Attack.Deserialize(element ?? Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
return SerializableProperties != null;
}
@@ -1137,8 +1136,8 @@ namespace Barotrauma
public override void Reset()
{
base.Reset();
Attack.Deserialize(OriginalElement);
Attack.ReloadAfflictions(OriginalElement);
Attack.Deserialize(OriginalElement, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
Attack.ReloadAfflictions(OriginalElement, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
}
public bool AddNewAffliction()
@@ -1149,7 +1148,7 @@ namespace Barotrauma
new XAttribute("strength", 0f),
new XAttribute("probability", 1.0f));
Element.Add(subElement);
Attack.ReloadAfflictions(Element);
Attack.ReloadAfflictions(Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
Serialize();
return true;
}
@@ -1158,7 +1157,7 @@ namespace Barotrauma
{
Serialize();
affliction.Remove();
Attack.ReloadAfflictions(Element);
Attack.ReloadAfflictions(Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
return Serialize();
}
}
@@ -2,6 +2,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
[NotSyncedInMultiplayer]
sealed class ItemAssemblyFile : GenericPrefabFile<ItemAssemblyPrefab>
{
public ItemAssemblyFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { }
@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
@@ -156,6 +156,19 @@ namespace Barotrauma
return -1;
}
public static void DisableMods(IReadOnlyCollection<ContentPackage> mods)
{
if (Core != null && mods.Contains(Core))
{
var newCore = ContentPackageManager.CorePackages.FirstOrDefault(p => !mods.Contains(p));
if (newCore != null)
{
SetCore(newCore);
}
}
SetRegular(Regular.Where(p => !mods.Contains(p)).ToArray());
}
public static void DisableRemovedMods()
{
if (Core != null && !ContentPackageManager.CorePackages.Contains(Core))
@@ -40,22 +40,25 @@ namespace Barotrauma
public readonly static PrefabCollection<EventSet> Prefabs = new PrefabCollection<EventSet>();
#if CLIENT
private static readonly Dictionary<string, Sprite> EventSprites = new Dictionary<string, Sprite>();
public static Sprite GetEventSprite(string identifier)
{
if (string.IsNullOrWhiteSpace(identifier)) { return null; }
foreach (var (key, value) in EventSprites)
if (EventSprite.Prefabs.TryGet(identifier.ToIdentifier(), out EventSprite sprite))
{
if (key.Equals(identifier, StringComparison.OrdinalIgnoreCase)) { return value; }
return sprite.Sprite;
}
#if DEBUG || UNSTABLE
DebugConsole.ThrowError($"Could not find the event sprite \"{identifier}\"");
#else
DebugConsole.AddWarning($"Could not find the event sprite \"{identifier}\"");
#endif
return null;
}
#endif
public static List<EventPrefab> GetAllEventPrefabs()
public static List<EventPrefab> GetAllEventPrefabs()
{
List<EventPrefab> eventPrefabs = EventPrefab.Prefabs.ToList();
foreach (var eventSet in Prefabs)
@@ -172,7 +172,7 @@ namespace Barotrauma
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
}
GiveReward();
if (level?.LevelData != null)
if (level.LevelData != null)
{
level.LevelData.IsBeaconActive = true;
}
@@ -95,23 +95,10 @@ namespace Barotrauma
partial void InitProjSpecific();
private GameSession(SubmarineInfo submarineInfo, List<SubmarineInfo>? ownedSubmarines = null)
private GameSession(SubmarineInfo submarineInfo)
{
InitProjSpecific();
SubmarineInfo = submarineInfo;
#if CLIENT
if (ownedSubmarines == null && GameMode is MultiPlayerCampaign && GameMain.NetLobbyScreen.ServerOwnedSubmarines != null)
{
ownedSubmarines = GameMain.NetLobbyScreen.ServerOwnedSubmarines;
}
#endif
OwnedSubmarines = ownedSubmarines ?? new List<SubmarineInfo>();
if (!OwnedSubmarines.Any(s => s.Name == submarineInfo.Name))
{
OwnedSubmarines.Add(submarineInfo);
}
GameMain.GameSession = this;
EventManager = new EventManager();
}
@@ -125,6 +112,7 @@ namespace Barotrauma
this.SavePath = savePath;
CrewManager = new CrewManager(gameModePreset.IsSinglePlayer);
GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, settings, missionType: missionType);
InitOwnedSubs(submarineInfo);
}
/// <summary>
@@ -135,12 +123,13 @@ namespace Barotrauma
{
CrewManager = new CrewManager(gameModePreset.IsSinglePlayer);
GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, CampaignSettings.Empty, missionPrefabs: missionPrefabs);
InitOwnedSubs(submarineInfo);
}
/// <summary>
/// Load a game session from the specified XML document. The session will be saved to the specified path.
/// </summary>
public GameSession(SubmarineInfo submarineInfo, List<SubmarineInfo> ownedSubmarines, XDocument doc, string saveFile) : this(submarineInfo, ownedSubmarines)
public GameSession(SubmarineInfo submarineInfo, List<SubmarineInfo> ownedSubmarines, XDocument doc, string saveFile) : this(submarineInfo)
{
this.SavePath = saveFile;
GameMain.GameSession = this;
@@ -173,6 +162,16 @@ namespace Barotrauma
break;
}
}
InitOwnedSubs(submarineInfo);
}
private void InitOwnedSubs(SubmarineInfo submarineInfo, List<SubmarineInfo>? ownedSubmarines = null)
{
OwnedSubmarines = ownedSubmarines ?? new List<SubmarineInfo>();
if (submarineInfo != null && !OwnedSubmarines.Any(s => s.Name == submarineInfo.Name))
{
OwnedSubmarines.Add(submarineInfo);
}
}
private GameMode InstantiateGameMode(GameModePreset gameModePreset, string? seed, SubmarineInfo selectedSub, CampaignSettings settings, IEnumerable<MissionPrefab>? missionPrefabs = null, MissionType missionType = MissionType.None)
@@ -295,7 +294,8 @@ namespace Barotrauma
Campaign!.GetWallet(client).TryDeduct(cost);
}
GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name);
Campaign!.PendingSubmarineSwitch = newSubmarine;
return newSubmarine;
}
@@ -116,8 +116,6 @@ namespace Barotrauma.Items.Components
this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
state = FabricatorState.Stopped;
InitProjSpecific();
}
public override void OnItemLoaded()
@@ -146,9 +144,6 @@ namespace Barotrauma.Items.Components
partial void OnItemLoadedProjSpecific();
partial void InitProjSpecific();
public override bool Select(Character character)
{
SelectProjSpecific(character);
@@ -192,7 +187,7 @@ namespace Barotrauma.Items.Components
if (!isClient)
{
MoveIngredientsToInputContainer(selectedItem);
if (selectedItem.RequiredMoney > 0)
if (selectedItem.RequiredMoney > 0 && CanBeFabricated(fabricatedItem, availableIngredients, user))
{
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign)
{
@@ -395,14 +390,17 @@ namespace Barotrauma.Items.Components
var fabricationitemAmount = new AbilityFabricationItemAmount(fabricatedItem.TargetItem, fabricatedItem.Amount);
int quality = 0;
if (user?.Info != null)
if (fabricatedItem.Quality.HasValue)
{
quality = fabricatedItem.Quality.Value;
}
else if (user?.Info != null)
{
foreach (Character character in Character.GetFriendlyCrew(user))
{
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
quality = GetFabricatedItemQuality(fabricatedItem, user);
}
@@ -1196,7 +1196,7 @@ namespace Barotrauma
drawableComponents.Add(drawable);
hasComponentsToDraw = true;
#if CLIENT
cachedVisibleSize = null;
cachedVisibleExtents = null;
#endif
}
}
@@ -1208,7 +1208,7 @@ namespace Barotrauma
drawableComponents.Remove(drawable);
hasComponentsToDraw = drawableComponents.Count > 0;
#if CLIENT
cachedVisibleSize = null;
cachedVisibleExtents = null;
#endif
}
}
@@ -118,6 +118,7 @@ namespace Barotrauma
public readonly ImmutableArray<Skill> RequiredSkills;
public readonly uint RecipeHash;
public readonly int Amount;
public readonly int? Quality;
/// <summary>
/// How many of this item the fabricator can create (< 0 = unlimited)
@@ -150,6 +151,11 @@ namespace Barotrauma
FabricationLimitMin = element.GetAttributeInt(nameof(FabricationLimitMin), limitDefault);
FabricationLimitMax = element.GetAttributeInt(nameof(FabricationLimitMax), limitDefault);
if (element.GetAttribute(nameof(Quality)) != null)
{
Quality = element.GetAttributeInt(nameof(Quality), 0);
}
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -15,7 +15,6 @@ namespace Barotrauma
public static readonly PrefabCollection<ItemAssemblyPrefab> Prefabs = new PrefabCollection<ItemAssemblyPrefab>();
public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies");
public static readonly string SaveFolder = "ItemAssemblies";
private readonly XElement configElement;
@@ -282,7 +282,7 @@ namespace Barotrauma
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level, Level.Cave parentCave = null)
{
float rotation = 0.0f;
if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null)
if (prefab.AlignWithSurface && spawnPosition != null && spawnPosition.Normal.LengthSquared() > 0.001f)
{
rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
}
@@ -359,8 +359,8 @@ namespace Barotrauma
/// How many map progress steps it takes before the discounts should be updated.
/// </summary>
private const int SpecialsUpdateInterval = 3;
private int DailySpecialsCount => Type.DailySpecialsCount;
private int RequestedGoodsCount => Type.RequestedGoodsCount;
public int DailySpecialsCount => Type.DailySpecialsCount;
public int RequestedGoodsCount => Type.RequestedGoodsCount;
private int StepsSinceSpecialsUpdated { get; set; }
public HashSet<Identifier> StoreIdentifiers { get; } = new HashSet<Identifier>();
@@ -1138,7 +1138,7 @@ namespace Barotrauma
{
store.Balance = Math.Min(store.Balance + (int)(StoreInitialBalance / 10.0f), StoreInitialBalance);
}
var stock = store.Stock;
var stock = new List<PurchasedItem>(store.Stock);
var stockToRemove = new List<PurchasedItem>();
foreach (var item in stock)
{
@@ -89,22 +89,18 @@ namespace Barotrauma
{
backwardsCompatibleIdentifier = $"merchant{backwardsCompatibleIdentifier}";
}
string[] storeIdentifiers = childElement.GetAttributeStringArray("storeidentifiers", new string[1] { backwardsCompatibleIdentifier });
foreach (string id in storeIdentifiers)
{
if (string.IsNullOrEmpty(id)) { continue; }
// TODO: Add some error messages if we have defined the min or max amount while the item is not sold
var priceInfo = new PriceInfo((int)(priceMultiplier * basePrice),
sold,
sold ? GetMinAmount(childElement, minAmount) : 0,
sold ? GetMaxAmount(childElement, maxAmount) : 0,
canBeSpecial,
storeMinLevelDifficulty,
storeBuyingMultiplier,
displayNonEmpty,
id);
priceInfos.Add(priceInfo);
}
string storeIdentifier = childElement.GetAttributeString("storeidentifier", backwardsCompatibleIdentifier);
// TODO: Add some error messages if we have defined the min or max amount while the item is not sold
var priceInfo = new PriceInfo((int)(priceMultiplier * basePrice),
sold,
sold ? GetMinAmount(childElement, minAmount) : 0,
sold ? GetMaxAmount(childElement, maxAmount) : 0,
canBeSpecial,
storeMinLevelDifficulty,
storeBuyingMultiplier,
displayNonEmpty,
storeIdentifier);
priceInfos.Add(priceInfo);
}
bool soldElsewhere = soldByDefault && element.GetAttributeBool("soldelsewhere", element.GetAttributeBool("soldeverywhere", false));
defaultPrice = new PriceInfo(basePrice,
@@ -313,7 +313,16 @@ namespace Barotrauma
for (float x = hull.Rect.X + diffFromHullEdge; x <= hull.Rect.Right - diffFromHullEdge; x += minDist)
{
var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
if (previousWaypoint != null) { wayPoint.ConnectTo(previousWaypoint); }
// Too close to stairs, will be assigned as a stair point -> remove
if (wayPoint.FindStairs() != null)
{
removals.Add(wayPoint);
continue;
}
if (previousWaypoint != null)
{
wayPoint.ConnectTo(previousWaypoint);
}
previousWaypoint = wayPoint;
}
if (previousWaypoint == null)
@@ -510,25 +519,29 @@ namespace Barotrauma
}
}
}
removals.ForEach(wp => wp.Remove());
removals.Clear();
// Stairs
foreach (MapEntity mapEntity in mapEntityList.ToList())
{
if (!(mapEntity is Structure structure)) { continue; }
if (structure.StairDirection == Direction.None) { continue; }
WayPoint[] stairPoints = new WayPoint[3];
float margin = -32;
stairPoints[0] = new WayPoint(
new Vector2(structure.Rect.X - 32.0f,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? 80 : structure.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);
stairPoints[0] = new WayPoint(new Vector2(
structure.Rect.X + 5,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? margin : structure.Rect.Height - 100)), SpawnType.Path, submarine);
stairPoints[1] = new WayPoint(
new Vector2(structure.Rect.Right + 32.0f,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? structure.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);
stairPoints[1] = new WayPoint(new Vector2(
structure.Rect.Right - 5,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? structure.Rect.Height - 100 : margin)), SpawnType.Path, submarine);
for (int i = 0; i < 2; i++)
{
for (int dir = -1; dir <= 1; dir += 2)
{
WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(100, 70));
WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(minDist * 1.5f, minDist / 2));
if (closest == null) { continue; }
stairPoints[i].ConnectTo(closest);
}
@@ -537,9 +550,8 @@ namespace Barotrauma
stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine);
stairPoints[0].ConnectTo(stairPoints[2]);
stairPoints[2].ConnectTo(stairPoints[1]);
stairPoints.ForEach(wp => wp.FindStairs());
}
removals.ForEach(wp => wp.Remove());
removals.Clear();
foreach (Item item in Item.ItemList)
{
@@ -840,7 +852,11 @@ namespace Barotrauma
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
if (body != null && body != ignoredBody && !(body.UserData is Submarine))
{
if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
if (body.UserData is Structure)
{
continue;
}
if (body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall) && body.UserData is Item i && i.GetComponent<Door>() != null)
{
continue;
}
@@ -960,14 +976,15 @@ namespace Barotrauma
FindStairs();
}
private void FindStairs()
private Structure FindStairs()
{
Stairs = null;
Body pickedBody = Submarine.PickBody(SimPosition, SimPosition - Vector2.UnitY * 2.0f, null, Physics.CollisionStairs);
Body pickedBody = Submarine.PickBody(SimPosition, SimPosition - new Vector2(0, 1.2f), null, Physics.CollisionStairs);
if (pickedBody != null && pickedBody.UserData is Structure structure && structure.StairDirection != Direction.None)
{
Stairs = structure;
Stairs = structure;
}
return Stairs;
}
public void InitializeLinks()
@@ -6,6 +6,25 @@ using System.Linq;
namespace Barotrauma.Networking
{
[NetworkSerialize]
struct TempClient : INetSerializableStruct
{
public string Name;
public Identifier PreferredJob;
public CharacterTeamType PreferredTeam;
public UInt16 NameID;
public UInt64 SteamID;
public byte ID;
public UInt16 CharacterID;
public float Karma;
public bool Muted;
public bool InGame;
public bool HasPermissions;
public bool IsOwner;
public bool AllowKicking;
public bool IsDownloading;
}
partial class Client : IDisposable
{
public const int MaxNameLength = 32;
@@ -58,10 +58,13 @@ namespace Barotrauma
cam.Position = Submarine.MainSub.WorldPosition;
cam.UpdateTransform(true);
}
GameMain.GameSession?.CrewManager?.AutoShowCrewList();
#endif
foreach (MapEntity entity in MapEntity.mapEntityList)
{
entity.IsHighlighted = false;
}
#if RUN_PHYSICS_IN_SEPARATE_THREAD
var physicsThread = new Thread(ExecutePhysics)
@@ -78,6 +81,10 @@ namespace Barotrauma
base.Deselect();
#if CLIENT
var config = GameSettings.CurrentConfig;
config.CrewMenuOpen = CrewManager.PreferCrewMenuOpen;
config.ChatOpen = ChatBox.PreferChatBoxOpen;
GameSettings.SetCurrentConfig(config);
GameSettings.SaveCurrentConfig();
GameMain.SoundManager.SetCategoryMuffle("default", false);
GUI.ClearMessages();
@@ -112,15 +112,17 @@ namespace Barotrauma.Steam
var toUninstall
= ContentPackageManager.WorkshopPackages.Where(p => p.SteamWorkshopId == workshopItem.Id)
.ToHashSet();
ContentPackageManager.EnabledPackages.DisableMods(toUninstall);
toUninstall.Select(p => p.Dir).ForEach(d => Directory.Delete(d));
ContentPackageManager.WorkshopPackages.Refresh();
ContentPackageManager.EnabledPackages.DisableRemovedMods();
}
public static async Task ForceRedownload(Steamworks.Ugc.Item item)
public static async Task ForceRedownload(Steamworks.Ugc.Item item, CancellationTokenSource? cancellationTokenSrc = null)
{
NukeDownload(item);
await item.DownloadAsync();
cancellationTokenSrc ??= new CancellationTokenSource();
await item.DownloadAsync(ct: cancellationTokenSrc.Token);
}
/// <summary>
@@ -57,6 +57,20 @@ namespace Barotrauma
return isCJK.IsMatch(text);
}
/// <summary>
/// Check if the currently selected language is available, and switch to English if not
/// </summary>
public static void VerifyLanguageAvailable()
{
if (!TextPacks.ContainsKey(GameSettings.CurrentConfig.Language))
{
DebugConsole.ThrowError($"Could not find the language \"{GameSettings.CurrentConfig.Language}\". Trying to switch to English...");
var config = GameSettings.CurrentConfig;
config.Language = "English".ToLanguageIdentifier();;
GameSettings.SetCurrentConfig(config);
}
}
public static bool ContainsTag(string tag)
{
return ContainsTag(tag.ToIdentifier());
+62
View File
@@ -1,3 +1,65 @@
---------------------------------------------------------------------------------------------------------
v0.17.6.0
---------------------------------------------------------------------------------------------------------
Changes:
- Buffed ethanol's and tobacco's effects.
- Renamed "details" to "manage" and "permissions" to "rank" in the client management context menu to make them a little more clear.
- Added an indicator for when players are downloading files from the server to the player list in the lobby.
- Adjustments, tweaks, and polish for the new abyss monster, now called "Latcher". Updated texture.
- Adjusted the kill hammerhead missions.
- Changes to character aiming behavior.
- Giant Spineling doesn't flee anymore when being shot with coilgun, chaingun, or small arms.
Changes (unstable only):
- Added separate icons for mods that you've published and mods that you've subscribed to.
- Added a button to the prompt asking you to download mods from the server that will subscribe to missing Workshop items.
- Added a context menu to the items in the Installed Mods tab.
- Added a button to update all mods that are out of date.
- Added a search box to the locked mods list.
- Added a search box to the required mods list in the submarine editor.
- Double-clicking now enables/disables items in the Installed Mods tab.
Fixes:
- Fixed swimming characters sometimes being unable to stand up on stairs/platforms even if the water is shallow enough.
- Fixed guitar and harmonica being rendered on top of the water effect.
- Fixed guitar, harmonica, accordion and captains pipe having neutral buoyancy.
- Fixed mid-round joining clients not seeing subs purchased during that round.
- Fixed research station being repairable by clicking on it instead of pressing E.
- Fixed medical curtains disappearing before they're off-screen.
- Fixed karma preset being forced to default when starting a new server.
- Fixed calyxanide not damaging the "naturally spawning" husks.
- Fixed Herja's rear motion detector being connected to an incorrect display, and the bottom turret display having an incorrect text.
- Fixed crash caused by selection not being cleared when autocompleting or running a console command.
- Waypointfixes on abandoned outpost modules, some regular outpost modules, and Winterhalter.
- Fixed bots occasionally getting stuck while climbing ladders connecting outpost modules.
- Fixes to waypoint generator, mainly on stairs.
- Fixed a null reference exception when a bot is dismissed while being told to follow the player and still in the combat state.
- Fixed Giant Spineling targeting doors after being attacked, which it shouldn't do by design. Might affect other creatures too.
Fixes (unstable only):
- Fixes and improvements to colony modules.
- Fixed items in vending machines not being displayed as "out of stock" client-side, fixed money getting deducted when trying to buy an item that's out of stock.
- Fixed crashing on startup if the selected language cannot be found (e.g. if you've previously used a modded language and no longer have that mod installed).
- Fixed chat messages about money transfer votes not showing up.
- Fixed voting not finishing until the timer has elapsed even if there's already enough yes/no votes.
- Fixed vitality multipliers not working (e.g. damage to the head not having a bigger effect than damage to the limbs).
- Fixed "create level object" crashing the level editor.
- Fixed crashing when trying to save a sub with whitespace at the end of the name.
- Fixed sub editor's tag picker not working.
- Fixed event sprites not appearing.
- Fixed submarine switching not working.
- Fixed crew list and chatbox refusing to stay closed.
- Fixed character names being in upper case when using the health scanner.
- Fixed explosive coilgun ammo not being sold by armory merchants.
- Fixed physicorium not being sold at research outposts.
- Fixed issues with store interface displaying incorrect information.
- Fixed issues with buying items in multiplayer campaign.
- Fixed issues with generating daily specials and requested goods for campaign stores.
- Fixed double title in mod download prompt.
- Fixed mod transfer skipping item assemblies.
- Waypoint fixes on new colony modules.
---------------------------------------------------------------------------------------------------------
v0.17.5.0
---------------------------------------------------------------------------------------------------------