(ae643deeb) Added alive checks to a couple of diving gear status effects (don't consume tanks when dead)

This commit is contained in:
Joonas Rikkonen
2019-04-01 22:47:22 +03:00
parent ec7e23061b
commit 2eaf22683d
129 changed files with 2451 additions and 1336 deletions
@@ -115,7 +115,7 @@ namespace Barotrauma
SonarLabel = element.GetAttributeString("sonarlabel", "");
}
public AITarget(Entity e)
public AITarget(Entity e, float sightRange = -1, float soundRange = 0)
{
Entity = e;
if (sightRange < 0)
File diff suppressed because it is too large Load Diff
@@ -193,7 +193,7 @@ namespace Barotrauma
// is not attached or is attached to something else
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
{
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.Range * enemyAI.AttackingLimb.attack.Range)
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
{
AttachToBody(character.AnimController.Collider, attachLimb, attachTargetBody, transformedAttachPos);
}
@@ -115,7 +115,8 @@ namespace Barotrauma
unreachable.Add(goToObjective.Target as Hull);
}
goToObjective = null;
SteeringManager.SteeringWander();
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
}
}
else if (currentHull != null)
@@ -99,7 +99,8 @@ namespace Barotrauma
FindTargetItem();
if (targetItem == null || moveToTarget == null)
{
SteeringManager.SteeringWander();
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
return;
}
@@ -98,63 +98,67 @@ namespace Barotrauma
PathSteering.Reset();
return;
}
if (standStillTimer < -walkDuration)
{
standStillTimer = Rand.Range(standStillMin, standStillMax);
}
//steer away from edges of the hull
if (character.AnimController.CurrentHull != null && !character.IsClimbing)
{
standStillTimer = Rand.Range(standStillMin, standStillMax);
}
Wander(deltaTime);
return;
}
if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance)
{
if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist));
}
else
{
PathSteering.Reset();
return;
}
}
else if (leftDist < WallAvoidDistance)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance-leftDist) / WallAvoidDistance);
PathSteering.WanderAngle = 0.0f;
return;
}
else
{
PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance-rightDist) / WallAvoidDistance);
PathSteering.WanderAngle = MathHelper.Pi;
return;
}
}
character.AIController.SteeringManager.SteeringWander();
if (!character.IsClimbing && !character.AnimController.InWater)
{
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
return;
}
if (currentTarget != null)
{
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition);
}
}
public void Wander(float deltaTime)
{
//steer away from edges of the hull
if (character.AnimController.CurrentHull != null && !character.IsClimbing)
{
float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X;
float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X;
if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance)
{
if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist));
}
else
{
PathSteering.Reset();
}
}
else if (leftDist < WallAvoidDistance)
{
//PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance - leftDist) / WallAvoidDistance);
PathSteering.SteeringManual(deltaTime, Vector2.UnitX);
PathSteering.WanderAngle = 0.0f;
}
else if (rightDist < WallAvoidDistance)
{
//PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance - rightDist) / WallAvoidDistance);
PathSteering.SteeringManual(deltaTime, -Vector2.UnitX);
PathSteering.WanderAngle = MathHelper.Pi;
}
else
{
SteeringManager.SteeringWander();
}
}
else
{
SteeringManager.SteeringWander();
}
if (!character.IsClimbing && !character.AnimController.InWater)
{
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
}
private readonly List<Hull> targetHulls = new List<Hull>(20);
private readonly List<float> hullWeights = new List<float>(20);
@@ -369,9 +369,18 @@ namespace Barotrauma
return;
}
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2;
float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle.Value : HeadAngle.Value) * Dir;
Vector2 transformedMovement = reverse ? -movement : movement;
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
float mainLimbAngle = 0;
if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
{
mainLimbAngle = TorsoAngle.Value;
}
else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
mainLimbAngle = HeadAngle.Value;
}
mainLimbAngle *= Dir;
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
{
movementAngle += MathHelper.TwoPi;
@@ -379,7 +388,7 @@ namespace Barotrauma
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
{
movementAngle -= MathHelper.TwoPi;
}
}
if (CurrentSwimParams.RotateTowardsMovement)
{
@@ -403,7 +412,6 @@ namespace Barotrauma
if (TailAngle.HasValue)
{
Limb tail = GetLimb(LimbType.Tail);
//tail?.body.SmoothRotate(movementAngle + TailAngle.Value * Dir, TailTorque);
if (tail != null)
{
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
@@ -413,6 +421,10 @@ namespace Barotrauma
else
{
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (reverse)
{
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
}
if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
@@ -442,7 +454,7 @@ namespace Barotrauma
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude);
if (waveLength > 0 && waveAmplitude > 0)
{
WalkPos -= movement.Length() / Math.Abs(waveLength);
WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
WalkPos = MathUtils.WrapAngleTwoPi(WalkPos);
}
@@ -685,12 +697,6 @@ namespace Barotrauma
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength);
}
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
{
angle -= MathHelper.TwoPi;
}
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
}
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
@@ -99,7 +99,7 @@ namespace Barotrauma
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml";
protected static string GetFolder(string speciesName)
public static string GetFolder(string speciesName)
{
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
@@ -79,7 +79,7 @@ namespace Barotrauma
new XAttribute("sourcerect", $"0, 0, 1, 1")))
};
protected static string GetFolder(string speciesName)
public static string GetFolder(string speciesName)
{
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
@@ -1035,8 +1035,6 @@ namespace Barotrauma
CheckValidity();
CheckValidity();
UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider();
UpdateCollisionCategories();
@@ -1297,17 +1295,42 @@ namespace Barotrauma
UpdateProjSpecific(deltaTime);
}
private void CheckValidity()
public bool Invalid { get; private set; }
private int validityResets;
private bool CheckValidity()
{
CheckValidity(Collider);
bool isColliderValid = CheckValidity(Collider);
bool limbsValid = true;
foreach (Limb limb in limbs)
{
if (limb.body == null || !limb.body.Enabled) { continue; }
CheckValidity(limb.body);
if (!CheckValidity(limb.body))
{
limbsValid = false;
break;
}
}
bool isValid = isColliderValid && limbsValid;
if (!isValid)
{
validityResets++;
if (validityResets > 1)
{
Invalid = true;
DebugConsole.ThrowError("Invalid ragdoll physics. Ragdoll freezed to prevent crashes.");
Collider.SetTransform(Vector2.Zero, 0.0f);
foreach (Limb limb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
}
Frozen = true;
}
}
return isValid;
}
private void CheckValidity(PhysicsBody body)
private bool CheckValidity(PhysicsBody body)
{
string errorMsg = null;
string bodyName = body.UserData is Limb ? "Limb" : "Collider";
@@ -1359,7 +1382,7 @@ namespace Barotrauma
limb.body.ResetDynamics();
}
SetInitialLimbPositions();
return;
return false;
}
return true;
}
@@ -28,6 +28,7 @@ namespace Barotrauma
public enum AIBehaviorAfterAttack
{
FallBack,
FallBackUntilCanAttack,
PursueIfCanAttack,
Pursue
}
@@ -81,6 +82,9 @@ namespace Barotrauma
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
public AIBehaviorAfterAttack AfterAttack { get; private set; }
[Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")]
public bool Reverse { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")]
public float Range { get; private set; }
@@ -96,6 +100,9 @@ namespace Barotrauma
[Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")]
public float SecondaryCoolDown { get; private set; } = 0;
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")]
public float CoolDownRandomFactor { get; private set; } = 0;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
public float StructureDamage { get; private set; }
@@ -182,7 +189,7 @@ namespace Barotrauma
public readonly List<Affliction> Afflictions = new List<Affliction>();
/// <summary>
/// Only affects ai decision making.
/// Only affects ai decision making. All the conditionals has to be met in order to select the attack. TODO: allow to define conditionals using any (implemented in StatusEffect -> move from there to PropertyConditional?)
/// </summary>
public List<PropertyConditional> Conditionals { get; private set; } = new List<PropertyConditional>();
@@ -444,8 +451,10 @@ namespace Barotrauma
public void SetCoolDown()
{
CoolDownTimer = CoolDown;
SecondaryCoolDownTimer = SecondaryCoolDown;
float randomFraction = CoolDown * CoolDownRandomFactor;
CoolDownTimer = CoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
randomFraction = SecondaryCoolDown * CoolDownRandomFactor;
SecondaryCoolDownTimer = SecondaryCoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
}
public void ResetCoolDown()
@@ -810,6 +810,7 @@ namespace Barotrauma
public void LoadHeadAttachments()
{
if (Info == null) { return; }
if (AnimController == null) { return; }
var head = AnimController.GetLimb(LimbType.Head);
if (head == null) { return; }
@@ -1112,13 +1113,15 @@ namespace Barotrauma
ViewTarget = null;
if (!AllowInput) return;
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
if (Controlled == this)
if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer))
{
SmoothedCursorPosition = cursorPosition;
}
else
{
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
}
@@ -1185,6 +1188,10 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F))
{
AnimController.ReleaseStuckLimbs();
if (AIController != null && AIController is EnemyAIController enemyAI)
{
enemyAI.LatchOntoAI?.DeattachFromBody();
}
}
#endif
@@ -1264,15 +1271,7 @@ namespace Barotrauma
if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this);
}
}
#if CLIENT
if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
{
//emulate a Select input to get the character to deselect the item server-side
keys[(int)InputType.Select].Hit = true;
SelectedConstruction = null;
}
#endif
if (SelectedConstruction != null)
{
if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this);
@@ -1650,7 +1649,8 @@ namespace Barotrauma
#if CLIENT
if (isLocalPlayer)
{
if (GUI.MouseOn == null && !CharacterInventory.IsMouseOnInventory())
if (GUI.MouseOn == null &&
(!CharacterInventory.IsMouseOnInventory() || CharacterInventory.DraggingItemToWorld))
{
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
{
@@ -1665,10 +1665,12 @@ namespace Barotrauma
focusedItem = null;
}
findFocusedTimer -= deltaTime;
}
}
#endif
//climb ladders automatically when pressing up/down inside their trigger area
if (SelectedConstruction == null && !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
if ((SelectedConstruction == null || currentLadder != null) &&
!AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
{
bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down);
bool isControlled = Controlled == this;
@@ -1679,6 +1681,19 @@ namespace Barotrauma
float minDist = float.PositiveInfinity;
foreach (Ladder ladder in Ladder.List)
{
if (ladder == currentLadder)
{
continue;
}
else if (currentLadder != null)
{
//only switch from ladder to another if the ladders are above the current ladders and pressing up, or vice versa
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y != IsKeyDown(InputType.Up))
{
continue;
}
}
if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist)
{
minDist = dist;
@@ -1695,7 +1710,7 @@ namespace Barotrauma
}
}
if (SelectedCharacter != null && IsKeyHit(InputType.Grab)) //Let people use ladders and buttons and stuff when dragging chars
if (SelectedCharacter != null && (IsKeyHit(InputType.Grab) || IsKeyHit(InputType.Health))) //Let people use ladders and buttons and stuff when dragging chars
{
DeselectCharacter();
}
@@ -804,6 +804,12 @@ namespace Barotrauma
var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true);
if (newItem == null) { continue; }
if (!MathUtils.NearlyEqual(newItem.Condition, newItem.MaxCondition) &&
GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(newItem, new object[] { NetEntityEvent.Type.Status });
}
int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 });
if (!slotIndices.Any())
{
@@ -153,7 +153,10 @@ namespace Barotrauma
//how high the strength has to be for the affliction icon to be shown in the UI
public readonly float ShowIconThreshold = 0.05f;
public readonly float MaxStrength = 100.0f;
//how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f;
public float BurnOverlayAlpha;
public float DamageOverlayAlpha;
@@ -257,6 +260,8 @@ namespace Barotrauma
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
@@ -460,6 +460,7 @@ namespace Barotrauma
{
affliction.Strength = 0.0f;
}
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction)
@@ -520,14 +520,15 @@ namespace Barotrauma
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
wasHit = true;
}
else if (damageTarget is Structure && structureBody?.UserData is Structure)
else if (damageTarget is Structure wall && structureBody != null &&
(structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine)))
{
// If the attack is aimed to a structure and hits a structure, it's successful
// If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful
wasHit = true;
}
else
{
// If the attack is aimed to a character but hits a structure, the hit is blocked.
// If there is nothing between, the hit is successful
wasHit = structureBody == null;
}
}
@@ -103,12 +103,6 @@ namespace Barotrauma
if (missionType == MissionType.Random)
{
allowedMissions.AddRange(MissionPrefab.List);
#if SERVER
if (GameMain.Server != null)
{
allowedMissions.RemoveAll(mission => !GameMain.Server.ServerSettings.AllowedRandomMissionTypes.Contains(mission.type));
}
#endif
}
else if (missionType == MissionType.None)
{
@@ -124,6 +118,11 @@ namespace Barotrauma
{
allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1]));
}
if (allowedMissions.Count == 0)
{
return null;
}
int probabilitySum = allowedMissions.Sum(m => m.Commonness);
int randomNumber = rand.NextInt32() % probabilitySum;
@@ -139,7 +139,7 @@ namespace Barotrauma
MTRandom rand = new MTRandom(ToolBox.StringToInt(seed));
for (int i = 0; i < 2; i++)
{
dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null);
dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null, rand);
}
}
@@ -208,7 +208,7 @@ namespace Barotrauma
DockingPort myPort = null, outPostPort = null;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal) { continue; }
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine == level.StartOutpost)
{
outPostPort = port;
@@ -880,8 +880,7 @@ namespace Barotrauma.Items.Components
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
{
Item linkedItem = entity as Item;
if (linkedItem == null) { continue; }
if (!(entity is Item linkedItem)) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null)
@@ -426,11 +426,11 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null)
{
if (!attachable || item.body == null) return (character == null || character.IsKeyDown(InputType.Aim));
if (!attachable || item.body == null) { return character == null || character.IsKeyDown(InputType.Aim); }
if (character != null)
{
if (!character.IsKeyDown(InputType.Aim)) return false;
if (!CanBeAttached()) return false;
if (!character.IsKeyDown(InputType.Aim)) { return false; }
if (!CanBeAttached()) { return false; }
#if SERVER
if (GameMain.Server != null)
{
@@ -438,7 +438,12 @@ namespace Barotrauma.Items.Components
GameServer.Log(character.LogName + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
}
#endif
item.Drop(character);
}
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
if (character != null) { item.Drop(character); }
AttachToWall();
}
return true;
@@ -550,7 +555,7 @@ namespace Barotrauma.Items.Components
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
base.ServerWrite(msg, c, extraData);
if (!attachable || body == null) return;
if (!attachable || body == null) { return; }
msg.Write(Attached);
msg.Write(body.SimPosition.X);
@@ -2,12 +2,15 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class LevelResource : ItemComponent, IServerSerializable
{
private float lastSentDeattachTimer;
[Serialize(1.0f, false)]
public float DeattachDuration
{
@@ -21,12 +24,29 @@ namespace Barotrauma.Items.Components
get { return deattachTimer; }
set
{
deattachTimer = Math.Max(0.0f, value);
//clients don't deattach the item until the server says so (handled in ClientRead)
if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) && deattachTimer >= DeattachDuration)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return;
}
deattachTimer = Math.Max(0.0f, value);
#if SERVER
if (deattachTimer >= DeattachDuration)
{
if (holdable.Attached){ item.CreateServerEvent(this); }
holdable.DeattachFromWall();
}
else if (Math.Abs(lastSentDeattachTimer - deattachTimer) > 0.1f)
{
item.CreateServerEvent(this);
lastSentDeattachTimer = deattachTimer;
}
#else
if (deattachTimer >= DeattachDuration)
{
holdable.DeattachFromWall();
}
#endif
}
}
@@ -67,7 +87,10 @@ namespace Barotrauma.Items.Components
return;
}
holdable.Reattachable = false;
holdable.PickingTime = float.MaxValue;
if (requiredItems.Any())
{
holdable.PickingTime = float.MaxValue;
}
var body = item.body ?? holdable.Body;
@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
private readonly List<string> fixableEntities;
private Vector2 pickedPosition;
private float activeTimer;
private Vector2 debugRayStartPos, debugRayEndPos;
[Serialize(0.0f, false)]
public float Range { get; set; }
@@ -156,7 +158,7 @@ namespace Barotrauma.Items.Components
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
if (RepairThroughWalls)
{
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false);
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
foreach (Body body in bodies)
{
FixBody(user, deltaTime, degreeOfSuccess, body);
@@ -164,7 +166,7 @@ namespace Barotrauma.Items.Components
}
else
{
FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false));
FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true));
}
if (ExtinguishAmount > 0.0f && item.CurrentHull != null)
@@ -247,6 +249,7 @@ namespace Barotrauma.Items.Components
var levelResource = targetItem.GetComponent<LevelResource>();
if (levelResource != null && levelResource.IsActive &&
levelResource.requiredItems.Any() &&
levelResource.HasRequiredItems(user, addMessage: false))
{
levelResource.DeattachTimer += deltaTime;
@@ -104,8 +104,8 @@ namespace Barotrauma.Items.Components
#if SERVER
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
item.Drop(picker, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
Character thrower = picker;
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f);
@@ -189,7 +189,7 @@ namespace Barotrauma.Items.Components
{
get { return name; }
}
[Editable, Serialize("", true)]
public string Msg
{
@@ -203,12 +203,6 @@ namespace Barotrauma.Items.Components
set;
}
public AITarget AITarget
{
get;
private set;
}
public AITarget AITarget
{
get;
@@ -253,7 +247,7 @@ namespace Barotrauma.Items.Components
{
DebugConsole.ThrowError("Invalid pick key in " + element + "!", e);
}
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ParseMsg();
@@ -155,13 +155,14 @@ namespace Barotrauma.Items.Components
{
if (item.Container != null) { return false; }
if (AutoInteractWithContained)
if (AutoInteractWithContained && character.SelectedConstruction == null)
{
foreach (Item contained in Inventory.Items)
{
if (contained == null) continue;
if (contained.TryInteract(character))
{
character.FocusedItem = contained;
return false;
}
}
@@ -178,6 +179,7 @@ namespace Barotrauma.Items.Components
if (contained == null) continue;
if (contained.TryInteract(picker))
{
picker.FocusedItem = contained;
return true;
}
}
@@ -90,7 +90,7 @@ namespace Barotrauma.Items.Components
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
if (Math.Abs(Force) > 1.0f)
{
Vector2 currForce = new Vector2((force / 100.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f);
Vector2 currForce = new Vector2((force / 10.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f);
//less effective when in a bad condition
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
private bool shutDown;
const float AIUpdateInterval = 1.0f;
const float AIUpdateInterval = 0.2f;
private float aiUpdateTimer;
private Character lastUser;
@@ -209,10 +209,10 @@ namespace Barotrauma.Items.Components
allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10);
float heatAmount = fissionRate * (AvailableFuel / 100.0f) * 2.0f;
float heatAmount = GetGeneratedHeat(fissionRate);
float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
//if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime);
TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime);
@@ -313,6 +313,48 @@ namespace Barotrauma.Items.Components
}
}
private float GetGeneratedHeat(float fissionRate)
{
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
}
/// <summary>
/// Do we need more fuel to generate enough power to match the current load.
/// </summary>
/// <param name="minimumOutputRatio">How low we allow the output/load ratio to go before loading more fuel.
/// 1.0 = always load more fuel when maximum output is too low, 0.5 = load more if max output is 50% of the load</param>
private bool NeedMoreFuel(float minimumOutputRatio)
{
if (prevAvailableFuel <= 0.0f && load > 0.0f)
{
return true;
}
//fission rate is clamped to the amount of available fuel
float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f);
float maxTurbineOutput = 100.0f;
//calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max
float theoreticalMaxHeat = GetGeneratedHeat(fissionRate: maxFissionRate);
float temperatureFactor = Math.Min(theoreticalMaxHeat / 50.0f, 1.0f);
float theoreticalMaxOutput = Math.Min(maxTurbineOutput / 100.0f, temperatureFactor) * MaxPowerOutput;
//maximum output not enough, we need more fuel
return theoreticalMaxOutput < load * minimumOutputRatio;
}
private bool TooMuchFuel()
{
var containedItems = item.ContainedItems;
if (containedItems != null && containedItems.Count() <= 1) { return false; }
//get the amount of heat we'd generate if the fission rate was at the low end of the optimal range
float minimumHeat = GetGeneratedHeat(optimalFissionRate.X);
//if we need a very high turbine output to keep the engine from overheating, there's too much fuel
return minimumHeat > Math.Min(correctTurbineOutput * 1.5f, 90);
}
private void UpdateFailures(float deltaTime)
{
if (temperature > allowedTemperature.Y)
@@ -367,6 +409,11 @@ namespace Barotrauma.Items.Components
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f);
}
targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f);
//don't push the target too far from the current fission rate
//otherwise we may "overshoot", cranking the target fission rate all the way up because it takes a while
//for the actual fission rate and temperature to follow
targetFissionRate = MathHelper.Clamp(targetFissionRate, FissionRate - 5, FissionRate + 5);
}
public override void UpdateBroken(float deltaTime, Camera cam)
@@ -441,12 +488,18 @@ namespace Barotrauma.Items.Components
}
}
//we need more fuel
if (-currPowerConsumption < load * 0.5f && prevAvailableFuel <= 0.0f)
if (aiUpdateTimer > 0.0f)
{
aiUpdateTimer -= deltaTime;
return false;
}
//load more fuel if the current maximum output is only 50% of the current load
if (NeedMoreFuel(minimumOutputRatio: 0.5f))
{
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>())
{
MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
GetItemPriority = (Item fuelItem) =>
{
if (fuelItem.ParentInventory?.Owner is Item)
@@ -461,6 +514,7 @@ namespace Barotrauma.Items.Components
character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
aiUpdateTimer = AIUpdateInterval;
return false;
}
else if (TooMuchFuel())
@@ -479,12 +533,6 @@ namespace Barotrauma.Items.Components
}
}
if (aiUpdateTimer > 0.0f)
{
aiUpdateTimer -= deltaTime;
return false;
}
if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
{
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
@@ -506,7 +554,7 @@ namespace Barotrauma.Items.Components
{
AutoTemp = false;
unsentChanges = true;
UpdateAutoTemp(2.0f + degreeOfSuccess * 5.0f, 1.0f);
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
}
#if CLIENT
onOffSwitch.BarScroll = 0.0f;
@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
}
continue;
}
if (!pt.IsActive) { continue; }
if (!pt.IsActive || !pt.CanTransfer) { continue; }
gridLoad += pt.PowerLoad;
gridPower -= pt.CurrPowerConsumption;
@@ -209,9 +209,9 @@ namespace Barotrauma.Items.Components
Charge -= CurrPowerOutput / 3600.0f;
}
item.SendSignal(0, Charge.ToString(), "charge", null);
item.SendSignal(0, ((Charge / capacity) * 100).ToString(), "charge_%", null);
item.SendSignal(0, ((RechargeSpeed / maxRechargeSpeed) * 100).ToString(), "charge_rate", null);
item.SendSignal(0, ((int)Charge).ToString(), "charge", null);
item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null);
item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
foreach (Pair<Powered, Connection> connected in directlyConnected)
{
@@ -180,7 +180,8 @@ namespace Barotrauma.Items.Components
#endif
//items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(Math.Min(OverloadVoltage, 1.0f), OverloadVoltage, item.Condition / item.MaxCondition);
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, item.Condition / item.MaxCondition);
maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
//if the item can't be fixed, don't allow it to break
if (!item.Repairables.Any() || !CanBeOverloaded) continue;
@@ -319,7 +320,13 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
//float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
RelayComponent thisRelayComponent = this as RelayComponent;
if (thisRelayComponent != null)
{
clampPower = Math.Min(Math.Min(clampPower, thisRelayComponent.MaxPower), powerLoad);
clampLoad = Math.Min(clampLoad, thisRelayComponent.MaxPower);
}
foreach (Connection c in PowerConnections)
{
@@ -357,6 +364,8 @@ namespace Barotrauma.Items.Components
continue;
}
float addLoad = 0.0f;
float addPower = 0.0f;
if (powered is PowerContainer powerContainer)
{
if (recipient.Name == "power_in")
@@ -365,7 +374,7 @@ namespace Barotrauma.Items.Components
}
else
{
fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower);
addPower = powerContainer.CurrPowerOutput;
}
}
else
@@ -380,10 +389,16 @@ namespace Barotrauma.Items.Components
//negative power consumption = the construction is a
//generator/battery or another junction box
{
fullPower -= Math.Max(powered.CurrPowerConsumption, -maxPower);
addPower -= powered.CurrPowerConsumption;
}
}
}
if (addPower + fullPower > clampPower) { addPower -= (addPower + fullPower) - clampPower; };
if (addPower > 0) { fullPower += addPower; }
if (addLoad + fullLoad > clampLoad) { addLoad -= (addLoad + fullLoad) - clampLoad; };
if (addLoad > 0) { fullLoad += addLoad; }
}
}
}
}
@@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f;
private string header;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer;
@@ -52,17 +50,20 @@ namespace Barotrauma.Items.Components
set;
}
/*private float repairProgress;
public float RepairProgress
[Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")]
public float FixDurationLowSkill
{
get { return repairProgress; }
set
{
repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f);
if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
}
}*/
get;
set;
}
[Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")]
public float FixDurationHighSkill
{
get;
set;
}
private Character currentFixer;
public Character CurrentFixer
{
@@ -83,8 +84,6 @@ namespace Barotrauma.Items.Components
this.item = item;
header = element.GetAttributeString("name", "");
fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f);
fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f);
InitProjSpecific(element);
}
@@ -160,7 +159,7 @@ namespace Barotrauma.Items.Components
}
bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor);
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition;
@@ -186,26 +185,5 @@ namespace Barotrauma.Items.Components
{
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
}
}
@@ -163,6 +163,14 @@ namespace Barotrauma.Items.Components
{
wires[index] = wire;
recipientsDirty = true;
if (wire != null)
{
var otherConnection = wire.OtherConnection(this);
if (otherConnection != null)
{
otherConnection.recipientsDirty = true;
}
}
}
public void SendSignal(int stepsTaken, string signal, Item source, Character sender, float power, float signalStrength = 1.0f)
@@ -356,7 +356,7 @@ namespace Barotrauma.Items.Components
projectile.body.ResetDynamics();
projectile.body.Enabled = true;
projectile.SetTransform(ConvertUnits.ToSimUnits(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y)), -rotation);
projectile.FindHull();
projectile.UpdateTransform();
projectile.Submarine = projectile.body.Submarine;
Projectile projectileComponent = projectile.GetComponent<Projectile>();
@@ -59,7 +59,7 @@ namespace Barotrauma
public PhysicsBody body;
public readonly XElement StaticBodyConfig;
private float lastSentCondition;
private float sendConditionUpdateTimer;
private bool conditionUpdatePending;
@@ -211,14 +211,14 @@ namespace Barotrauma
set { spriteColor = value; }
}
[Serialize("1.0,1.0,1.0,1.0", false), Editable]
[Serialize("1.0,1.0,1.0,1.0", true), Editable]
public Color InventoryIconColor
{
get;
protected set;
}
[Serialize("1.0,1.0,1.0,1.0", false), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
[Serialize("1.0,1.0,1.0,1.0", true), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
public Color ContainerColor
{
get;
@@ -274,12 +274,11 @@ namespace Barotrauma
SetActiveSprite();
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition))
{
if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
conditionUpdatePending = true;
}
}
}
@@ -996,6 +995,21 @@ namespace Barotrauma
aiTarget.SightRange -= deltaTime * 1000.0f;
aiTarget.SoundRange -= deltaTime * 1000.0f;
}
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending)
{
if (sendConditionUpdateTimer <= 0.0f)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval;
conditionUpdatePending = false;
}
}
}
ApplyStatusEffects(ActionType.Always, deltaTime, null);
@@ -1272,7 +1286,11 @@ namespace Barotrauma
LastSentSignalRecipients.Clear();
if (connections == null) return;
stepsTaken++;
public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
{
List<T> connectedComponents = new List<T>();
List<Item> alreadySearched = new List<Item>() { this };
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
if (!connections.TryGetValue(connectionName, out Connection c)) return;
@@ -554,6 +554,39 @@ namespace Barotrauma
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
if (sprite == null)
{
DebugConsole.ThrowError("Item \"" + Name + "\" has no sprite!");
#if SERVER
sprite = new Sprite("", Vector2.Zero);
sprite.SourceRect = new Rectangle(0, 0, 32, 32);
#else
sprite = new Sprite(TextureLoader.PlaceHolderTexture, null, null)
{
Origin = TextureLoader.PlaceHolderTexture.Bounds.Size.ToVector2() / 2
};
#endif
size = sprite.size;
sprite.EntityID = identifier;
}
if (!category.HasFlag(MapEntityCategory.Legacy) && string.IsNullOrEmpty(identifier))
{
DebugConsole.ThrowError(
"Item prefab \"" + name + "\" has no identifier. All item prefabs have a unique identifier string that's used to differentiate between items during saving and loading.");
}
if (!string.IsNullOrEmpty(identifier))
{
MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == identifier);
if (existingPrefab != null)
{
DebugConsole.ThrowError(
"Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!");
}
}
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
List.Add(this);
}
@@ -28,8 +28,10 @@ namespace Barotrauma
public Explosion(float range, float force, float damage, float structureDamage, float empStrength = 0.0f)
{
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range);
attack.SeverLimbsProbability = 1.0f;
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range)
{
SeverLimbsProbability = 1.0f
};
this.force = force;
this.empStrength = empStrength;
sparks = true;
@@ -183,9 +185,6 @@ namespace Barotrauma
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
@@ -175,12 +175,11 @@ namespace Barotrauma
LimitSize();
UpdateProjSpecific(growModifier);
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
}
partial void UpdateProjSpecific(float growModifier);
@@ -293,10 +292,6 @@ namespace Barotrauma
//evaporate some of the water
hull.WaterVolume -= extinguishAmount;
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
@@ -325,12 +320,11 @@ namespace Barotrauma
size.X -= extinguishAmount;
hull.WaterVolume -= extinguishAmount;
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
}
public void Extinguish(float deltaTime, float amount, Vector2 worldPosition)
@@ -88,6 +88,7 @@ namespace Barotrauma
Gap.UpdateHulls();
}
OxygenPercentage = prevOxygenPercentage;
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
Pressure = surface;
}
@@ -231,7 +231,7 @@ namespace Barotrauma
public static Level CreateRandom(LocationConnection locationConnection)
{
string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name;
string seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName;
float sizeFactor = MathUtils.InverseLerp(
MapGenerationParams.Instance.SmallLevelConnectionLength,
@@ -1522,21 +1522,49 @@ namespace Barotrauma
outpost.MakeOutpost();
Point? minSize = null;
DockingPort subPort = null;
if (Submarine.MainSub != null)
{
Point subSize = Submarine.MainSub.GetDockedBorders().Size;
Point outpostSize = outpost.GetDockedBorders().Size;
minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y);
float closestDistance = float.MaxValue;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine != Submarine.MainSub) { continue; }
//the submarine port has to be at the top of the sub
if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; }
float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X);
if (dist < closestDistance)
{
subPort = port;
closestDistance = dist;
}
}
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize));
float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X;
//don't try to compensate if the port is very far from the sub's center of mass
if (Math.Abs(subDockingPortOffset) > 2000.0f)
{
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f);
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
DebugConsole.NewMessage(warningMsg, Color.Orange);
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg);
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset));
if ((i == 0) == !Mirrored)
{
StartOutpost = outpost;
if (GameMain.GameSession?.StartLocation != null) { outpost.Name = GameMain.GameSession.StartLocation.Name; }
}
else
{
EndOutpost = outpost;
if (GameMain.GameSession?.EndLocation != null) { outpost.Name = GameMain.GameSession.EndLocation.Name; }
}
}
}
@@ -15,7 +15,6 @@ namespace Barotrauma
public Vector3 Position;
public float NetworkUpdateTimer;
public const float NetworkUpdateInterval = 0.2f;
public float Scale;
@@ -343,7 +343,7 @@ namespace Barotrauma
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj });
obj.NeedsNetworkSyncing = false;
obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval;
obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval;
}
}
@@ -432,12 +432,16 @@ namespace Barotrauma
{
if (ForceFluctuationStrength > 0.0f)
{
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
//no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers
if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any())
{
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
{
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
}
}
}
@@ -16,6 +16,8 @@ namespace Barotrauma
public int TypeChangeTimer;
public string BaseName { get => baseName; }
public string Name { get; private set; }
public Vector2 MapPosition { get; private set; }
@@ -32,10 +34,10 @@ namespace Barotrauma
get
{
CheckMissionCompleted();
for (int i = availableMissions.Count; i < Connections.Count * 2; i++)
{
int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue;
int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue;
MTRandom rand = new MTRandom(seed);
LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count];
@@ -46,7 +48,7 @@ namespace Barotrauma
if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; }
if (GameSettings.VerboseLogging && mission != null)
{
DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White);
DebugConsole.NewMessage("Generated a new mission for a location (location: " + Name + ", seed: " + seed.ToString("X") + ", missions completed: " + MissionsCompleted + ", type: " + mission.Name + ")", Color.White);
}
availableMissions.Add(mission);
}
@@ -75,10 +77,10 @@ namespace Barotrauma
}
}
public Location(Vector2 mapPosition, int? zone)
public Location(Vector2 mapPosition, int? zone, Random rand)
{
this.Type = LocationType.Random("", zone);
this.Name = RandomName(Type);
this.Type = LocationType.Random(rand, zone);
this.Name = RandomName(Type, rand);
this.MapPosition = mapPosition;
PortraitId = ToolBox.StringToInt(Name);
@@ -86,9 +88,9 @@ namespace Barotrauma
Connections = new List<LocationConnection>();
}
public static Location CreateRandom(Vector2 position, int? zone)
public static Location CreateRandom(Vector2 position, int? zone , Random rand)
{
return new Location(position, zone);
return new Location(position, zone, rand);
}
public IEnumerable<Mission> GetMissionsInConnection(LocationConnection connection)
@@ -99,7 +101,16 @@ namespace Barotrauma
public void ChangeType(LocationType newType)
{
if (newType == Type) return;
if (newType == Type) { return; }
//clear missions from this and adjacent locations (they may be invalid now)
availableMissions.Clear();
foreach (LocationConnection connection in Connections)
{
connection.OtherLocation(this)?.availableMissions.Clear();
}
DebugConsole.Log("Location " + baseName + " changed it's type from " + Type + " to " + newType);
Type = newType;
Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName);
@@ -111,6 +122,7 @@ namespace Barotrauma
{
if (mission.Completed)
{
DebugConsole.Log("Mission \"" + mission.Name + "\" completed in \"" + Name + "\".");
MissionsCompleted++;
}
}
@@ -118,10 +130,10 @@ namespace Barotrauma
availableMissions.RemoveAll(m => m.Completed);
}
private string RandomName(LocationType type)
private string RandomName(LocationType type, Random rand)
{
baseName = type.GetRandomName();
nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server);
baseName = type.GetRandomName(rand);
nameFormatIndex = rand.Next() % type.NameFormats.Count;
return type.NameFormats[nameFormatIndex].Replace("[name]", baseName);
}
@@ -155,20 +155,15 @@ namespace Barotrauma
return portraits[Math.Abs(portraitId) % portraits.Count];
}
public string GetRandomName()
{
return names[Rand.Int(names.Count, Rand.RandSync.Server)];
}
public static LocationType Random(string seed = "", int? zone = null)
{
Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
public string GetRandomName(Random rand)
{
return names[rand.Next() % names.Count];
}
public static LocationType Random(Random rand, int? zone = null)
{
Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
List<LocationType> allowedLocationTypes = zone.HasValue ? List.FindAll(lt => lt.CommonnessPerZone.ContainsKey(zone.Value)) : List;
if (allowedLocationTypes.Count == 0)
@@ -180,12 +175,12 @@ namespace Barotrauma
{
return ToolBox.SelectWeightedRandom(
allowedLocationTypes,
allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(),
Rand.RandSync.Server);
allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(),
rand);
}
else
{
return allowedLocationTypes[Rand.Int(allowedLocationTypes.Count, Rand.RandSync.Server)];
return allowedLocationTypes[rand.Next() % allowedLocationTypes.Count];
}
}
@@ -196,7 +196,7 @@ namespace Barotrauma
Vector2 position = points[positionIndex];
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) position = points[1 - positionIndex];
int zone = MathHelper.Clamp(generationParams.DifficultyZones - (int)Math.Floor(Vector2.Distance(position, mapCenter) / zoneRadius), 1, generationParams.DifficultyZones);
newLocations[i] = Location.CreateRandom(position, zone);
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.Server));
Locations.Add(newLocations[i]);
}
@@ -578,10 +578,12 @@ namespace Barotrauma
location.MissionsCompleted = missionsCompleted;
if (showNotifications && prevLocationType != location.Type)
{
ChangeLocationType(
location,
prevLocationName,
prevLocationType.CanChangeTo.Find(c => c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant()));
var change = prevLocationType.CanChangeTo.Find(c =>
c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant());
if (change != null)
{
ChangeLocationType(location, prevLocationName, change);
}
}
break;
case "connection":
@@ -297,7 +297,13 @@ namespace Barotrauma
CreateStairBodies();
}
}
// Only add ai targets automatically to walls
if (aiTarget == null && HasBody && Tags.Contains("wall"))
{
aiTarget = new AITarget(this);
}
InsertToList();
}
@@ -217,6 +217,10 @@ namespace Barotrauma
}
SerializableProperty.DeserializeProperties(sp, element);
if (sp.Body)
{
sp.Tags.Add("wall");
}
string translatedDescription = TextManager.Get("EntityDescription." + sp.identifier, true);
if (!string.IsNullOrEmpty(translatedDescription)) sp.Description = translatedDescription;
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Voronoi2;
@@ -493,7 +494,7 @@ namespace Barotrauma
}
}
public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null)
public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null, float subDockingPortOffset = 0.0f)
{
Rectangle dockedBorders = GetDockedBorders();
Vector2 diffFromDockedBorders =
@@ -532,6 +533,10 @@ namespace Barotrauma
{
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
}
else
{
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
}
}
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
@@ -554,6 +559,26 @@ namespace Barotrauma
spawnPos.X = (minX + maxX) / 2;
}
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
{
//no walls found at either side, just use the initial spawnpos and hope for the best
}
else if (minX < 0)
{
//no wall found at the left side, spawn to the left from the right-side wall
spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset;
}
else if (maxX > Level.Loaded.Size.X)
{
//no wall found at right side, spawn to the right from the left-side wall
spawnPos.X = minX + minWidth + 100.0f + subDockingPortOffset;
}
else
{
//walls found at both sides, use their midpoint
spawnPos.X = (minX + maxX) / 2 + subDockingPortOffset;
}
spawnPos.Y = Math.Min(spawnPos.Y, Level.Loaded.Size.Y - dockedBorders.Height / 2 - 10);
return spawnPos - diffFromDockedBorders;
}
@@ -665,7 +690,7 @@ namespace Barotrauma
}
}
public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null, bool allowInsideFixture = false)
{
if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f)
{
@@ -677,21 +702,20 @@ namespace Barotrauma
Body closestBody = null;
if (allowInsideFixture)
{
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem) return -1;
if (customPredicate != null && !customPredicate(fixture)) return -1;
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1;
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) =>
{
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
if (fixture.Body.UserData is Structure structure)
closestFraction = 0.0f;
closestNormal = Vector2.Normalize(rayEnd - rayStart);
if (fixture.Body != null) closestBody = fixture.Body;
return false;
}, ref aabb);
if (closestFraction <= 0.0f)
{
lastPickedPosition = rayStart;
lastPickedFraction = closestFraction;
@@ -721,7 +745,7 @@ namespace Barotrauma
return closestBody;
}
public static List<Body> PickBodies(Vector2 rayStart, Vector2 rayEnd, List<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
public static List<Body> PickBodies(Vector2 rayStart, Vector2 rayEnd, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null, bool allowInsideFixture = false)
{
if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f)
{
@@ -732,25 +756,9 @@ namespace Barotrauma
List<Body> bodies = new List<Body>();
GameMain.World.RayCast((fixture, point, normal, fraction) =>
{
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem) return -1;
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return -1; }
if (customPredicate != null && !customPredicate(fixture)) return -1;
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1;
if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1;
if (fixture.Body.UserData is Structure structure)
{
if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform)) return -1;
}
bodies.Add(fixture.Body);
if (fixture.Body != null) { bodies.Add(fixture.Body); }
if (fraction < closestFraction)
{
lastPickedPosition = rayStart + (rayEnd - rayStart) * fraction;
@@ -760,10 +768,68 @@ namespace Barotrauma
return fraction;
}, rayStart, rayEnd);
if (allowInsideFixture)
{
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) =>
{
if (bodies.Contains(fixture.Body) || fixture.Body == null) { return true; }
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
closestFraction = 0.0f;
lastPickedPosition = rayStart;
lastPickedFraction = 0.0f;
lastPickedNormal = Vector2.Normalize(rayEnd - rayStart);
bodies.Add(fixture.Body);
return false;
}, ref aabb);
}
return bodies;
}
private static bool CheckFixtureCollision(Fixture fixture, IEnumerable<Body> ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate<Fixture> customPredicate = null)
{
if (fixture == null ||
(ignoreSensors && fixture.IsSensor) ||
fixture.CollisionCategories == Category.None ||
fixture.CollisionCategories == Physics.CollisionItem)
{
return false;
}
if (customPredicate != null && !customPredicate(fixture))
{
return false;
}
if (collisionCategory != null &&
!fixture.CollisionCategories.HasFlag((Category)collisionCategory) &&
!((Category)collisionCategory).HasFlag(fixture.CollisionCategories))
{
return false;
}
if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body))
{
return false;
}
if (fixture.Body.UserData is Structure structure)
{
if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform))
{
return false;
}
}
return true;
}
/// <summary>
/// check visibility between two points (in sim units)
/// </summary>
@@ -615,7 +615,7 @@ namespace Barotrauma
ID = (ushort)int.Parse(element.Attribute("ID").Value)
};
Enum.TryParse(element.GetAttributeString("spawn", "Path"), out w.spawnType);
w.spawnType = spawnType;
string idCardDescString = element.GetAttributeString("idcarddesc", "");
if (!string.IsNullOrWhiteSpace(idCardDescString))
@@ -7,7 +7,7 @@
enum FileTransferMessageType
{
Unknown, Initiate, Data, Cancel
Unknown, Initiate, Data, TransferOnSameMachine, Cancel
}
enum FileTransferType
@@ -32,11 +32,14 @@ namespace Barotrauma.Networking
public const float HighPrioCharacterPositionUpdateInterval = 0.0f;
public const float LowPrioCharacterPositionUpdateInterval = 1.0f;
//how much the physics body of an item has to move until the server
//send a position update to clients (in sim units)
public const float ItemPosUpdateDistance = 2.0f;
public const float DeleteDisconnectedTime = 10.0f;
public const float DeleteDisconnectedTime = 20.0f;
public const float ItemConditionUpdateInterval = 0.15f;
public const float LevelObjectUpdateInterval = 0.5f;
public const float HullUpdateInterval = 0.5f;
public const float HullUpdateDistance = 20000.0f;
public const int MaxEventPacketsPerUpdate = 4;
/// <summary>
/// Interpolates the positional error of a physics body towards zero.
@@ -58,15 +58,8 @@ namespace Barotrauma.Networking
eventCount++;
continue;
}
//the ID has been taken by another entity (the original entity has been removed) -> write an empty event
/*else if (Entity.FindEntityByID(e.Entity.ID) != e.Entity || e.Entity.IdFreed)
{
//technically the clients don't have any use for these, but removing events and shifting the IDs of all
//consecutive ones is so error-prone that I think this is a safer option
tempBuffer.Write(Entity.NullEntityID);
tempBuffer.WritePadBits();
}*/
else
if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength)
{
//no more room in this packet
break;
@@ -4,11 +4,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
#if CLIENT
using Barotrauma.Particles;
using Barotrauma.Sounds;
#endif
namespace Barotrauma
{
@@ -91,52 +86,10 @@ namespace Barotrauma
}
}
private TargetType targetTypes;
protected HashSet<string> targetIdentifiers;
public readonly ItemPrefab ItemPrefab;
public readonly SpawnPositionType SpawnPosition;
public readonly float Speed;
public readonly float Rotation;
public ItemSpawnInfo(XElement element, string parentDebugName)
{
if (element.Attribute("name") != null)
{
//backwards compatibility
DebugConsole.ThrowError("Error in StatusEffect config (" + element.ToString() + ") - use item identifier instead of the name.");
string itemPrefabName = element.GetAttributeString("name", "");
ItemPrefab = MapEntityPrefab.List.Find(m => m is ItemPrefab && (m.NameMatches(itemPrefabName) || m.Tags.Contains(itemPrefabName))) as ItemPrefab;
if (ItemPrefab == null)
{
DebugConsole.ThrowError("Error in StatusEffect \""+ parentDebugName + "\" - item prefab \"" + itemPrefabName + "\" not found.");
}
}
else
{
string itemPrefabIdentifier = element.GetAttributeString("identifier", "");
if (string.IsNullOrEmpty(itemPrefabIdentifier)) itemPrefabIdentifier = element.GetAttributeString("identifiers", "");
if (string.IsNullOrEmpty(itemPrefabIdentifier))
{
DebugConsole.ThrowError("Invalid item spawn in StatusEffect \"" + parentDebugName + "\" - identifier not found in the element \"" + element.ToString() + "\"");
}
ItemPrefab = MapEntityPrefab.List.Find(m => m is ItemPrefab && m.Identifier == itemPrefabIdentifier) as ItemPrefab;
if (ItemPrefab == null)
{
DebugConsole.ThrowError("Error in StatusEffect config - item prefab with the identifier \"" + itemPrefabIdentifier + "\" not found.");
return;
}
}
Speed = element.GetAttributeFloat("speed", 0.0f);
Rotation = MathHelper.ToRadians(element.GetAttributeFloat("rotation", 0.0f));
private List<RoundSound> sounds = new List<RoundSound>();
private SoundSelectionMode soundSelectionMode;
private SoundChannel soundChannel;
private bool loopSound;
#endif
private List<RelatedItem> requiredItems;
public string[] propertyNames;
@@ -228,10 +181,6 @@ namespace Barotrauma
Range = element.GetAttributeFloat("range", 0.0f);
#if CLIENT
particleEmitters = new List<ParticleEmitter>();
#endif
IEnumerable<XAttribute> attributes = element.Attributes();
List<XAttribute> propertyAttributes = new List<XAttribute>();
propertyConditionals = new List<PropertyConditional>();
@@ -408,25 +357,6 @@ namespace Barotrauma
var newSpawnItem = new ItemSpawnInfo(subElement, parentDebugName);
if (newSpawnItem.ItemPrefab != null) spawnItems.Add(newSpawnItem);
break;
#if CLIENT
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
break;
case "sound":
var sound = Submarine.LoadRoundSound(subElement);
if (sound != null)
{
loopSound = subElement.GetAttributeBool("loop", false);
if (subElement.Attribute("selectionmode") != null)
{
if (Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode))
{
soundSelectionMode = selectionMode;
}
}
sounds.Add(sound);
}
break;
}
}
InitProjSpecific(element, parentDebugName);
@@ -439,11 +369,6 @@ namespace Barotrauma
return (targetTypes & targetType) != 0;
}
public bool HasTargetType(TargetType targetType)
{
return (targetTypes & targetType) != 0;
}
public virtual bool HasRequiredItems(Entity entity)
{
if (requiredItems == null) return true;
@@ -643,46 +568,10 @@ namespace Barotrauma
{
hull = ((Item)entity).CurrentHull;
}
#if CLIENT
if (entity != null && sounds.Count > 0)
{
if (soundChannel == null || !soundChannel.IsPlaying)
{
if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (RoundSound sound in sounds)
{
soundChannel = SoundPlayer.PlaySound(sound.Sound, sound.Volume, sound.Range, entity.WorldPosition, hull);
if (soundChannel != null) soundChannel.Looping = loopSound;
}
}
else
{
int selectedSoundIndex = 0;
if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
{
selectedSoundIndex = item.ID % sounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
{
selectedSoundIndex = user.ID % sounds.Count;
}
else
{
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, selectedSound.Volume, selectedSound.Range, entity.WorldPosition, hull);
if (soundChannel != null) soundChannel.Looping = loopSound;
}
}
}
#endif
foreach (ISerializableEntity serializableEntity in targets)
{
Item item = serializableEntity as Item;
if (item == null) continue;
if (!(serializableEntity is Item item)) continue;
Character targetCharacter = targets.FirstOrDefault(t => t is Character character && !character.Removed) as Character;
if (targetCharacter == null)
@@ -782,11 +671,7 @@ namespace Barotrauma
fire.Size = new Vector2(FireSize, fire.Size.Y);
}
bool isNotClient = true;
#if CLIENT
isNotClient = GameMain.Client == null;
#endif
bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient;
if (isNotClient && entity != null && Entity.Spawner != null) //clients are not allowed to spawn items
{
foreach (ItemSpawnInfo itemSpawnInfo in spawnItems)
@@ -843,27 +728,11 @@ namespace Barotrauma
}
}
#if CLIENT
if (entity != null)
{
foreach (ParticleEmitter emitter in particleEmitters)
{
float angle = 0.0f;
if (emitter.Prefab.CopyEntityAngle)
{
if (entity is Item it)
{
angle = it.body == null ? 0.0f : it.body.Rotation;
}
}
emitter.Emit(deltaTime, entity.WorldPosition, hull, angle);
}
}
ApplyProjSpecific(deltaTime, entity, targets, hull);
}
partial void ApplyProjSpecific(float deltaTime, Entity entity, List<ISerializableEntity> targets, Hull currentHull);
private void ApplyToProperty(ISerializableEntity target, SerializableProperty property, object value, float deltaTime)
{
if (disableDeltaTime || setValue) deltaTime = 1.0f;
@@ -403,18 +403,18 @@ namespace Barotrauma
}
}
public static void ClearFolder(string FolderName, string[] ignoredFiles = null)
public static void ClearFolder(string FolderName, string[] ignoredFileNames = null)
{
DirectoryInfo dir = new DirectoryInfo(FolderName);
foreach (FileInfo fi in dir.GetFiles())
{
if (ignoredFiles != null)
if (ignoredFileNames != null)
{
bool ignore = false;
foreach (string ignoredFile in ignoredFiles)
foreach (string ignoredFile in ignoredFileNames)
{
if (Path.GetFullPath(fi.FullName).Equals(Path.GetFullPath(ignoredFile)))
if (Path.GetFileName(fi.FullName).Equals(Path.GetFileName(ignoredFile)))
{
ignore = true;
break;