Build 0.18.4.0

This commit is contained in:
Markus Isberg
2022-05-31 23:13:05 +09:00
parent 077917fa5d
commit 64db1a6a44
175 changed files with 4916 additions and 2393 deletions
@@ -360,9 +360,6 @@ namespace Barotrauma
{
if (allowedSlot.HasFlag(SlotTypes[i]) && item.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) && slots[i].Items.Any(it => it != item))
{
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
#endif
if (!slots[i].First().AllowedSlots.Contains(InvSlotType.Any) || !TryPutItem(slots[i].FirstOrDefault(), character, new List<InvSlotType> { InvSlotType.Any }, true, ignoreCondition))
{
free = false;
@@ -382,9 +379,6 @@ namespace Barotrauma
{
if (allowedSlot.HasFlag(SlotTypes[i]) && item.GetComponents<Pickable>().Any(p => p.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i]))) && slots[i].Empty())
{
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
#endif
bool removeFromOtherSlots = item.ParentInventory != this;
if (placedInSlot == -1 && inWrongSlot)
{
@@ -454,9 +448,6 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("CharacterInventory.TryPutItem:IndexOutOfRange", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return false;
}
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[index])) { hidePersonalSlots = false; }
#endif
//there's already an item in the slot
if (slots[index].Any())
{
@@ -480,9 +471,6 @@ namespace Barotrauma
foreach (InvSlotType allowedSlot in pickable.AllowedSlots)
{
if (!allowedSlot.HasFlag(SlotTypes[index])) { continue; }
#if CLIENT
if (PersonalSlots.HasFlag(allowedSlot)) { hidePersonalSlots = false; }
#endif
for (int i = 0; i < capacity; i++)
{
if (allowedSlot.HasFlag(SlotTypes[i]) && slots[i].Any() && !slots[i].Contains(item))
@@ -144,9 +144,16 @@ namespace Barotrauma.Items.Components
}
}
public bool Combine(GeneticMaterial otherGeneticMaterial, Character user)
public enum CombineResult
{
if (!CanBeCombinedWith(otherGeneticMaterial)) { return false; }
None,
Refined,
Combined
}
public CombineResult Combine(GeneticMaterial otherGeneticMaterial, Character user)
{
if (!CanBeCombinedWith(otherGeneticMaterial)) { return CombineResult.None; }
float conditionIncrease = Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
conditionIncrease += user?.GetStatValue(StatTypes.GeneticMaterialRefineBonus) ?? 0.0f;
@@ -158,7 +165,7 @@ namespace Barotrauma.Items.Components
{
MakeTainted();
}
return true;
return CombineResult.Refined;
}
else
{
@@ -171,7 +178,7 @@ namespace Barotrauma.Items.Components
{
MakeTainted();
}
return false;
return CombineResult.Combined;
}
}
@@ -697,14 +697,17 @@ namespace Barotrauma.Items.Components
Vector2 fromCharacterToLeak = leak.WorldPosition - character.AnimController.AimSourceWorldPos;
float dist = fromCharacterToLeak.Length();
float reach = AIObjectiveFixLeak.CalculateReach(this, character);
if (dist > reach * 3)
if (dist > reach * 2)
{
// Too far away -> consider this done and hope the AI is smart enough to move closer
Reset();
return true;
}
character.AIController.SteeringManager.Reset();
if (character.AIController.SteeringManager is IndoorsSteeringManager pathSteering)
{
pathSteering.ResetPath();
}
if (!character.AnimController.InWater)
{
// TODO: use the collider size?
@@ -714,34 +717,25 @@ namespace Barotrauma.Items.Components
humanAnim.Crouching = true;
}
}
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
if (!character.IsClimbing)
{
// Steer closer
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
{
// Swimming inside the sub
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && (indoorSteering.CurrentPath.Unreachable || indoorSteering.CurrentPath.Finished))
// Steer closer
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
if (!character.InWater)
{
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
}
else
{
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
dir.Y = 0;
}
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
}
else
else if (dist < reach * 0.25f && !character.IsClimbing)
{
// Swimming outside the sub
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
// Too close -> steer away
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
}
}
else if (dist < reach * 0.25f)
{
// Too close -> steer away
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
}
if (dist <= reach)
if (dist <= reach || character.IsClimbing)
{
// In range
character.CursorPosition = leak.WorldPosition;
@@ -815,7 +809,7 @@ namespace Barotrauma.Items.Components
}
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Max(s => s.damage) < 0.1f);
if (leakFixed && leak.FlowTargetHull?.DisplayName != null && character.IsOnPlayerTeam)
{
@@ -187,7 +187,12 @@ namespace Barotrauma.Items.Components
[Serialize(false, IsPropertySaveable.No)]
public bool RemoveContainedItemsOnDeconstruct { get; set; }
private SlotRestrictions[] slotRestrictions;
private readonly ImmutableArray<SlotRestrictions> slotRestrictions;
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
private Vector2 prevContainedItemPositions;
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
{
@@ -237,10 +242,11 @@ namespace Barotrauma.Items.Components
}
}
Inventory = new ItemInventory(item, this, totalCapacity, SlotsPerRow);
slotRestrictions = new SlotRestrictions[totalCapacity];
List<SlotRestrictions> newSlotRestrictions = new List<SlotRestrictions>(totalCapacity);
for (int i = 0; i < capacity; i++)
{
slotRestrictions[i] = new SlotRestrictions(maxStackSize, ContainableItems);
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems));
}
int subContainerIndex = capacity;
@@ -268,11 +274,13 @@ namespace Barotrauma.Items.Components
for (int i = subContainerIndex; i < subContainerIndex + subCapacity; i++)
{
slotRestrictions[i] = new SlotRestrictions(subMaxStackSize, subContainableItems);
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems));
}
subContainerIndex += subCapacity;
}
capacity = totalCapacity;
slotRestrictions = newSlotRestrictions.ToImmutableArray();
System.Diagnostics.Debug.Assert(totalCapacity == slotRestrictions.Length);
InitProjSpecific(element);
}
@@ -365,18 +373,21 @@ namespace Barotrauma.Items.Components
return false;
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public override void Update(float deltaTime, Camera cam)
{
if (!string.IsNullOrEmpty(SpawnWithId) && !alwaysContainedItemsSpawned)
{
SpawnAlwaysContainedItems();
alwaysContainedItemsSpawned = true;
}
if (item.ParentInventory is CharacterInventory ownerInventory)
{
item.SetContainedItemPositions();
if (Vector2.DistanceSquared(prevContainedItemPositions, item.Position) > 10.0f)
{
SetContainedItemPositions();
prevContainedItemPositions = item.Position;
}
if (AutoInject)
{
@@ -397,7 +408,7 @@ namespace Barotrauma.Items.Components
item.body.Enabled &&
item.body.FarseerBody.Awake)
{
item.SetContainedItemPositions();
SetContainedItemPositions();
}
else if (activeContainedItems.Count == 0)
{
@@ -147,7 +147,7 @@ namespace Barotrauma.Items.Components
CancelUsing(user);
user = null;
}
if (!IsToggle) { IsActive = false; }
if (!IsToggle || item.Connections == null) { IsActive = false; }
return;
}
@@ -231,28 +231,40 @@ namespace Barotrauma.Items.Components
if (targetItem == otherItem) { continue; }
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r == otherItem.Prefab.Identifier))
{
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
foreach (Character character in Character.GetFriendlyCrew(user))
{
character.CheckTalents(AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
}
var geneticMaterial1 = targetItem.GetComponent<GeneticMaterial>();
var geneticMaterial2 = otherItem.GetComponent<GeneticMaterial>();
if (geneticMaterial1 != null && geneticMaterial2 != null)
{
if (geneticMaterial1.Combine(geneticMaterial2, user))
var result = geneticMaterial1.Combine(geneticMaterial2, user);
if (result == GeneticMaterial.CombineResult.Refined)
{
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddItemToRemoveQueue(otherItem);
}
if (result != GeneticMaterial.CombineResult.None)
{
OnCombinedOrRefined();
}
allowRemove = false;
return;
}
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddItemToRemoveQueue(otherItem);
else
{
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddItemToRemoveQueue(otherItem);
OnCombinedOrRefined();
}
}
}
void OnCombinedOrRefined()
{
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
foreach (Character character in Character.GetFriendlyCrew(user))
{
character.CheckTalents(AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
}
}
}
@@ -400,8 +400,8 @@ namespace Barotrauma.Items.Components
private void IncreaseSkillLevel(Character user, float deltaTime)
{
if (user?.Info == null) { return; }
// Do not increase the helm skill when "steering" the sub in an outpost level
if (GameMain.GameSession?.Campaign != null && Level.IsLoadedOutpost) { return; }
// Do not increase the helm skill when "steering" the sub while docked into something static (e.g. outpost or wreck)
if (GameMain.GameSession?.Campaign != null && controlledSub != null && controlledSub.DockedTo.Any(d => d.PhysicsBody.BodyType == BodyType.Static)) { return; }
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
user.Info.IncreaseSkillLevel(
@@ -3,7 +3,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
@@ -81,6 +80,8 @@ namespace Barotrauma.Items.Components
private ItemContainer? container;
private float growthTickTimer;
private List<LightComponent>? lightComponents;
public Planter(Item item, ContentXElement element) : base(item, element)
{
canBePicked = true;
@@ -107,10 +108,14 @@ namespace Barotrauma.Items.Components
base.OnItemLoaded();
IsActive = true;
#if CLIENT
lightComponent = item.GetComponent<LightComponent>();
if (lightComponent != null)
var lights = item.GetComponents<LightComponent>();
if (lights.Any())
{
lightComponent.Light.Enabled = false;
lightComponents = lights.ToList();
foreach (var light in lightComponents)
{
light.Light.Enabled = false;
}
}
#endif
container = item.GetComponent<ItemContainer>();
@@ -227,12 +232,17 @@ namespace Barotrauma.Items.Components
base.Update(deltaTime, cam);
#if CLIENT
if (lightComponent != null)
if (lightComponents != null && lightComponents.Count > 0)
{
bool hasSeed = false;
foreach (Growable? seed in GrowableSeeds) { hasSeed |= seed != null; }
lightComponent.Light.Enabled = hasSeed;
foreach (Growable? seed in GrowableSeeds)
{
hasSeed |= seed != null;
}
foreach (var light in lightComponents)
{
light.Light.Enabled = hasSeed;
}
}
#endif
@@ -602,7 +602,7 @@ namespace Barotrauma.Items.Components
private bool ShouldDeteriorate()
{
if (Level.IsLoadedOutpost) { return false; }
if (Level.IsLoadedFriendlyOutpost) { return false; }
if (LastActiveTime > Timing.TotalTime) { return true; }
foreach (ItemComponent ic in item.Components)
@@ -13,12 +13,13 @@ namespace Barotrauma.Items.Components
private float updateTimer;
[Flags]
public enum TargetType
{
Any,
Human,
Monster,
Wall
Human = 1,
Monster = 2,
Wall = 4,
Any = Human | Monster | Wall,
}
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
@@ -179,6 +180,11 @@ namespace Barotrauma.Items.Components
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(new Signal(signalOut, 1), "state_out"); }
if (MotionDetected)
{
ApplyStatusEffects(ActionType.OnUse, deltaTime);
}
updateTimer -= deltaTime;
if (updateTimer > 0.0f) { return; }
@@ -199,8 +205,7 @@ namespace Barotrauma.Items.Components
float broadRangeX = Math.Max(rangeX * 2, 500);
float broadRangeY = Math.Max(rangeY * 2, 500);
if (item.CurrentHull == null && item.Submarine != null &&
(Target == TargetType.Wall || Target == TargetType.Any))
if (item.CurrentHull == null && item.Submarine != null && Target.HasFlag(TargetType.Wall))
{
if (Level.Loaded != null && (Math.Abs(item.Submarine.Velocity.X) > MinimumVelocity || Math.Abs(item.Submarine.Velocity.Y) > MinimumVelocity))
{
@@ -248,7 +253,7 @@ namespace Barotrauma.Items.Components
}
}
if (Target != TargetType.Wall)
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Monster))
{
foreach (Character c in Character.CharacterList)
{
@@ -258,14 +263,13 @@ namespace Barotrauma.Items.Components
//makes it possible to detect when a spawned character moves without triggering the detector immediately as the ragdoll spawns and drops to the ground
if (c.SpawnTime > Timing.TotalTime - 1.0) { continue; }
switch (Target)
if (c.IsHuman)
{
case TargetType.Human:
if (!c.IsHuman) { continue; }
break;
case TargetType.Monster:
if (c.IsHuman || c.IsPet) { continue; }
break;
if (!Target.HasFlag(TargetType.Human)) { continue; }
}
else if (!c.IsPet)
{
if (!Target.HasFlag(TargetType.Monster)) { continue; }
}
//do a rough check based on the position of the character's collider first
@@ -4,6 +4,7 @@ using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma.Items.Components
@@ -12,6 +13,46 @@ namespace Barotrauma.Items.Components
{
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The maximum amount of force applied to the triggering entitites.", alwaysUseInstanceValues: true)]
public float Force { get; set; }
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Determines if the force gets higher the closer the triggerer is to the center of the trigger.", alwaysUseInstanceValues: true)]
public bool DistanceBasedForce { get; set; }
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Determines if the force fluctuates over time or if it stays constant.", alwaysUseInstanceValues: true)]
public bool ForceFluctuation { get; set; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "How much the fluctuation affects the force. 1 is the maximum fluctuation, 0 is no fluctuation.", alwaysUseInstanceValues: true)]
private float ForceFluctuationStrength
{
get
{
return forceFluctuationStrength;
}
set
{
forceFluctuationStrength = Math.Clamp(value, 0.0f, 1.0f);
}
}
[Serialize(1.0f, IsPropertySaveable.Yes, description: "How fast (cycles per second) the force fluctuates.", alwaysUseInstanceValues: true)]
private float ForceFluctuationFrequency
{
get
{
return forceFluctuationFrequency;
}
set
{
forceFluctuationFrequency = Math.Max(value, 0.01f);
}
}
[Serialize(0.01f, IsPropertySaveable.Yes, description: "How often (in seconds) the force fluctuation is calculated.", alwaysUseInstanceValues: true)]
private float ForceFluctuationInterval
{
get
{
return forceFluctuationInterval;
}
set
{
forceFluctuationInterval = Math.Max(value, 0.01f);
}
}
public PhysicsBody PhysicsBody { get; private set; }
private float Radius { get; set; }
@@ -38,11 +79,6 @@ namespace Barotrauma.Items.Components
private readonly LevelTrigger.TriggererType triggeredBy;
private readonly HashSet<Entity> triggerers = new HashSet<Entity>();
private readonly bool triggerOnce;
private readonly bool distanceBasedForce;
private readonly bool forceFluctuation;
private readonly float forceFluctuationStrength;
private readonly float forceFluctuationFrequency;
private readonly float forceFluctuationInterval;
private readonly List<ISerializableEntity> statusEffectTargets = new List<ISerializableEntity>();
/// <summary>
/// Effects applied to entities inside the trigger
@@ -53,6 +89,10 @@ namespace Barotrauma.Items.Components
/// </summary>
private readonly List<Attack> attacks = new List<Attack>();
private float forceFluctuationStrength;
private float forceFluctuationFrequency;
private float forceFluctuationInterval;
public TriggerComponent(Item item, ContentXElement element) : base(item, element)
{
string triggeredByAttribute = element.GetAttributeString("triggeredby", "Character");
@@ -61,15 +101,6 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError($"Error in ForceComponent config: \"{triggeredByAttribute}\" is not a valid triggerer type.");
}
triggerOnce = element.GetAttributeBool("triggeronce", false);
distanceBasedForce = element.GetAttributeBool("distancebasedforce", false);
forceFluctuation = element.GetAttributeBool("forcefluctuation", false);
forceFluctuationStrength = element.GetAttributeFloat("forcefluctuationstrength", 1.0f);
forceFluctuationStrength = Math.Clamp(forceFluctuationStrength, 0.0f, 1.0f);
forceFluctuationFrequency = element.GetAttributeFloat("fluctuationfrequency", 1.0f);
forceFluctuationFrequency = Math.Max(forceFluctuationFrequency, 0.01f);
forceFluctuationInterval = element.GetAttributeFloat("fluctuationinterval", 0.01f);
forceFluctuationInterval = Math.Max(forceFluctuationInterval, 0.01f);
string parentDebugName = $"TriggerComponent in {item.Name}";
foreach (var subElement in element.Elements())
{
@@ -153,14 +184,14 @@ namespace Barotrauma.Items.Components
TriggerActive = triggerers.Any();
if (forceFluctuation && TriggerActive && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
if (ForceFluctuation && TriggerActive && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
ForceFluctuationTimer += deltaTime;
if (ForceFluctuationTimer >= forceFluctuationInterval)
if (ForceFluctuationTimer >= ForceFluctuationInterval)
{
float v = MathF.Sin(2 * MathF.PI * forceFluctuationFrequency * TimeInLevel);
float v = MathF.Sin(2 * MathF.PI * ForceFluctuationFrequency * TimeInLevel);
float amount = MathUtils.InverseLerp(-1.0f, 1.0f, v);
CurrentForceFluctuation = MathHelper.Lerp(1.0f - forceFluctuationStrength, 1.0f, amount);
CurrentForceFluctuation = MathHelper.Lerp(1.0f - ForceFluctuationStrength, 1.0f, amount);
ForceFluctuationTimer = 0.0f;
GameMain.NetworkMember?.CreateEntityEvent(this);
}
@@ -179,7 +210,7 @@ namespace Barotrauma.Items.Components
LevelTrigger.ApplyAttacks(attacks, item.WorldPosition, deltaTime);
}
if (Force < 0.01f)
if (Math.Abs(Force) < 0.01f)
{
// Just ignore very minimal forces
continue;
@@ -205,7 +236,7 @@ namespace Barotrauma.Items.Components
{
Vector2 diff = ConvertUnits.ToDisplayUnits(PhysicsBody.SimPosition - body.SimPosition);
if (diff.LengthSquared() < 0.0001f) { return; }
float distanceFactor = distanceBasedForce ? LevelTrigger.GetDistanceFactor(body, PhysicsBody, RadiusInDisplayUnits) : 1.0f;
float distanceFactor = DistanceBasedForce ? LevelTrigger.GetDistanceFactor(body, PhysicsBody, RadiusInDisplayUnits) : 1.0f;
if (distanceFactor <= 0.0f) { return; }
Vector2 force = distanceFactor * (CurrentForceFluctuation * Force) * Vector2.Normalize(diff);
if (force.LengthSquared() < 0.01f) { return; }
@@ -227,5 +258,43 @@ namespace Barotrauma.Items.Components
PhysicsBody.Submarine = item.Submarine;
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
base.ReceiveSignal(signal, connection);
switch (connection.Name)
{
case "set_force":
if (!FloatTryParse(signal, out float force)) { break; }
Force = force;
break;
case "set_distancebasedforce":
if (!bool.TryParse(signal.value, out bool distanceBasedForce)) { break; }
DistanceBasedForce = distanceBasedForce;
break;
case "set_forcefluctuation":
if (!bool.TryParse(signal.value, out bool forceFluctuation)) { break; }
ForceFluctuation = forceFluctuation;
break;
case "set_forcefluctuationstrength":
if (!FloatTryParse(signal, out float forceFluctuationStrength)) { break; }
ForceFluctuationStrength = forceFluctuationStrength;
break;
case "set_forcefluctuationfrequency":
if (!FloatTryParse(signal, out float forceFluctuationFrequency)) { break; }
ForceFluctuationFrequency = forceFluctuationFrequency;
break;
case "set_forcefluctuationinterval":
if (!FloatTryParse(signal, out float forceFluctuationInterval)) { break; }
ForceFluctuationInterval = forceFluctuationInterval;
break;
}
static bool FloatTryParse(Signal signal, out float value)
{
return float.TryParse(signal.value, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
}
}
}
}
}
@@ -4,9 +4,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using FarseerPhysics.Dynamics;
@@ -20,8 +18,6 @@ namespace Barotrauma.Items.Components
private Vector2 barrelPos;
private Vector2 transformedBarrelPos;
private LightComponent lightComponent;
private float rotation, targetRotation;
@@ -71,6 +67,8 @@ namespace Barotrauma.Items.Components
public Character ActiveUser;
private float resetActiveUserTimer;
private List<LightComponent> lightComponents;
public float Rotation
{
get { return rotation; }
@@ -168,10 +166,13 @@ namespace Barotrauma.Items.Components
rotation = (minRotation + maxRotation) / 2;
#if CLIENT
if (lightComponent != null)
if (lightComponents != null)
{
lightComponent.Rotation = rotation;
lightComponent.Light.Rotation = -rotation;
foreach (var light in lightComponents)
{
light.Rotation = rotation;
light.Light.Rotation = -rotation;
}
}
#endif
}
@@ -331,27 +332,39 @@ namespace Barotrauma.Items.Components
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
targetRotation = rotation;
FindLightComponent();
UpdateTransformedBarrelPos();
}
private void FindLightComponent()
private void FindLightComponents()
{
if (lightComponents != null)
{
// Can't run again, because of reparenting.
return;
}
foreach (LightComponent lc in item.GetComponents<LightComponent>())
{
// Only make the Turret control the LightComponents that are it's children. So it'd be possible to for example have some extra lights on the turret that don't rotate with it.
if (lc?.Parent == this)
{
lightComponent = lc;
break;
if (lightComponents == null)
{
lightComponents = new List<LightComponent>();
}
lightComponents.Add(lc);
}
}
#if CLIENT
if (lightComponent != null)
if (lightComponents != null)
{
lightComponent.Parent = null;
lightComponent.Rotation = Rotation - item.RotationRad;
lightComponent.Light.Rotation = -rotation;
foreach (var light in lightComponents)
{
// We want the turret to control the state of the LightComponent, not tie it's state to the state of the Turret (the light can be inactive even if the turret is active)
light.Parent = null;
light.Rotation = Rotation - item.RotationRad;
light.Light.Rotation = -rotation;
}
}
#endif
}
@@ -428,7 +441,7 @@ namespace Barotrauma.Items.Components
if (MathUtils.NearlyEqual(minRotation, maxRotation))
{
UpdateLightComponent();
UpdateLightComponents();
return;
}
@@ -452,7 +465,7 @@ namespace Barotrauma.Items.Components
}
// Do not increase the weapons skill when operating a turret in an outpost level
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedFriendlyOutpost))
{
user.Info.IncreaseSkillLevel("weapons".ToIdentifier(),
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f));
@@ -509,14 +522,17 @@ namespace Barotrauma.Items.Components
aiFindTargetTimer -= deltaTime;
}
UpdateLightComponent();
UpdateLightComponents();
}
private void UpdateLightComponent()
private void UpdateLightComponents()
{
if (lightComponent != null)
if (lightComponents != null)
{
lightComponent.Rotation = Rotation - item.RotationRad;
foreach (var light in lightComponents)
{
light.Rotation = Rotation - item.RotationRad;
}
}
}
@@ -1601,21 +1617,24 @@ namespace Barotrauma.Items.Components
}
break;
case "toggle_light":
if (lightComponent != null && signal.value != "0")
if (lightComponents != null && signal.value != "0")
{
lightComponent.IsOn = !lightComponent.IsOn;
UpdateLightComponent();
foreach (var light in lightComponents)
{
light.IsOn = !light.IsOn;
}
UpdateLightComponents();
}
break;
case "set_light":
if (lightComponent != null)
if (lightComponents != null)
{
bool shouldBeOn = signal.value != "0";
if (shouldBeOn != lightComponent.IsOn)
foreach (var light in lightComponents)
{
lightComponent.IsOn = shouldBeOn;
UpdateLightComponent();
light.IsOn = shouldBeOn;
}
UpdateLightComponents();
}
break;
}
@@ -1633,7 +1652,7 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
base.OnItemLoaded();
FindLightComponent();
FindLightComponents();
targetRotation = rotation;
if (!loadedBaseRotation.HasValue)
{
@@ -6,7 +6,6 @@ using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using System.Collections.Immutable;
using Barotrauma.Abilities;
namespace Barotrauma
@@ -82,7 +81,20 @@ namespace Barotrauma
public string Sound { get; private set; }
public Point? SheetIndex { get; private set; }
public LightComponent LightComponent { get; set; }
public LightComponent LightComponent => LightComponents?.FirstOrDefault();
public List<LightComponent> LightComponents
{
get
{
if (_lightComponents == null)
{
_lightComponents = new List<LightComponent>();
}
return _lightComponents;
}
}
private List<LightComponent> _lightComponents;
public int Variant { get; set; }
@@ -338,11 +350,14 @@ namespace Barotrauma.Items.Components
foreach (var lightElement in subElement.Elements())
{
if (!lightElement.Name.ToString().Equals("lightcomponent", StringComparison.OrdinalIgnoreCase)) { continue; }
wearableSprites[i].LightComponent = new LightComponent(item, lightElement)
wearableSprites[i].LightComponents.Add(new LightComponent(item, lightElement)
{
Parent = this
};
item.AddComponent(wearableSprites[i].LightComponent);
});
foreach (var light in wearableSprites[i].LightComponents)
{
item.AddComponent(light);
}
}
i++;
@@ -413,7 +428,10 @@ namespace Barotrauma.Items.Components
IsActive = true;
if (wearableSprite.LightComponent != null)
{
wearableSprite.LightComponent.ParentBody = equipLimb.body;
foreach (var light in wearableSprite.LightComponents)
{
light.ParentBody = equipLimb.body;
}
}
limb[i] = equipLimb;
@@ -467,7 +485,10 @@ namespace Barotrauma.Items.Components
if (wearableSprites[i].LightComponent != null)
{
wearableSprites[i].LightComponent.ParentBody = null;
foreach (var light in wearableSprites[i].LightComponents)
{
light.ParentBody = null;
}
}
equipLimb.WearingItems.RemoveAll(w => w != null && w == wearableSprites[i]);
@@ -494,7 +515,6 @@ namespace Barotrauma.Items.Components
}
item.SetTransform(picker.SimPosition, 0.0f);
item.SetContainedItemPositions();
item.ApplyStatusEffects(ActionType.OnWearing, deltaTime, picker);
@@ -24,13 +24,18 @@ namespace Barotrauma
partial class Item : MapEntity, IDamageable, IIgnorable, ISerializableEntity, IServerPositionSync, IClientSerializable
{
public static List<Item> ItemList = new List<Item>();
private static readonly HashSet<Item> dangerousItems = new HashSet<Item>();
public static IReadOnlyCollection<Item> DangerousItems { get { return dangerousItems; } }
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
public static bool ShowLinks = true;
private readonly HashSet<Identifier> tags;
private bool isWire, isLogic;
private readonly bool isWire, isLogic;
private Hull currentHull;
public Hull CurrentHull
@@ -279,7 +284,10 @@ namespace Barotrauma
if (Screen.Selected == GameMain.SubEditorScreen)
{
SetContainedItemPositions();
GetComponent<LightComponent>()?.SetLightSourceTransform();
foreach (var light in GetComponents<LightComponent>())
{
light.SetLightSourceTransform();
}
}
#endif
}
@@ -1001,6 +1009,10 @@ namespace Barotrauma
InsertToList();
ItemList.Add(this);
if (Prefab.IsDangerous)
{
dangerousItems.Add(this);
}
DebugConsole.Log("Created " + Name + " (" + ID + ")");
@@ -1092,17 +1104,16 @@ namespace Barotrauma
component.OnActiveStateChanged += (bool isActive) =>
{
bool hasSounds = false;
bool needsSoundUpdate = false;
#if CLIENT
hasSounds = component.HasSounds;
needsSoundUpdate = component.NeedsSoundUpdate();
#endif
//component doesn't need to be updated if it isn't active, doesn't have a parent that could activate it,
//nor status effects, sounds or conditionals that would need to run
//nor sounds or conditionals that would need to run
if (!isActive && !component.UpdateWhenInactive &&
!hasSounds &&
!needsSoundUpdate &&
component.Parent == null &&
(component.IsActiveConditionals == null || !component.IsActiveConditionals.Any()) &&
(component.statusEffectLists == null || !component.statusEffectLists.Any()))
(component.IsActiveConditionals == null || !component.IsActiveConditionals.Any()))
{
if (updateableComponents.Contains(component)) { updateableComponents.Remove(component); }
}
@@ -1499,6 +1510,11 @@ namespace Barotrauma
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, bool checkCondition = true, Vector2? worldPosition = null)
{
if (effect.intervalTimer > 0.0f)
{
effect.intervalTimer -= deltaTime;
return;
}
if (!isNetworkEvent && checkCondition)
{
if (condition == 0.0f && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { return; }
@@ -1630,6 +1646,7 @@ namespace Barotrauma
foreach (ItemComponent ic in components)
{
ic.PlaySound(ActionType.OnBroken);
ic.StopSounds(ActionType.OnActive);
}
if (Screen.Selected == GameMain.SubEditorScreen) { return; }
#endif
@@ -1722,6 +1739,19 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
#if SERVER
if (!(Submarine is { Loading: true }))
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending && sendConditionUpdateTimer <= 0.0f)
{
SendPendingNetworkUpdates();
}
}
#endif
if (!isActive) { return; }
if (impactQueue != null)
{
while (impactQueue.TryDequeue(out float impact))
@@ -1730,22 +1760,11 @@ namespace Barotrauma
}
}
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && (!Submarine?.Loading ?? true))
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending && sendConditionUpdateTimer <= 0.0f)
{
SendPendingNetworkUpdates();
}
}
if (aiTarget != null)
if (aiTarget != null && aiTarget.NeedsUpdate)
{
aiTarget.Update(deltaTime);
}
if (!isActive) { return; }
ApplyStatusEffects(ActionType.Always, deltaTime, character: (parentInventory as CharacterInventory)?.Owner as Character);
ApplyStatusEffects(parentInventory == null ? ActionType.OnNotContained : ActionType.OnContained, deltaTime, character: (parentInventory as CharacterInventory)?.Owner as Character);
@@ -1846,7 +1865,10 @@ namespace Barotrauma
}
else
{
if (updateableComponents.Count == 0 && !hasStatusEffectsOfType[(int)ActionType.Always] && (body == null || !body.Enabled))
if (updateableComponents.Count == 0 &&
(aiTarget == null || !aiTarget.NeedsUpdate) &&
!hasStatusEffectsOfType[(int)ActionType.Always] &&
(body == null || !body.Enabled))
{
#if CLIENT
positionBuffer.Clear();
@@ -1983,6 +2005,7 @@ namespace Barotrauma
impactQueue ??= new ConcurrentQueue<float>();
impactQueue.Enqueue(impact);
isActive = true;
return true;
}
@@ -2501,11 +2524,9 @@ namespace Barotrauma
if (ic.Use(deltaTime, character))
{
ic.WasUsed = true;
#if CLIENT
ic.PlaySound(ActionType.OnUse, character);
#endif
ic.PlaySound(ActionType.OnUse, character);
#endif
ic.ApplyStatusEffects(ActionType.OnUse, deltaTime, character, targetLimb);
if (ic.DeleteOnUse) { remove = true; }
@@ -2534,11 +2555,9 @@ namespace Barotrauma
if (ic.SecondaryUse(deltaTime, character))
{
ic.WasSecondaryUsed = true;
#if CLIENT
ic.PlaySound(ActionType.OnSecondaryUse, character);
#endif
ic.ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, character);
if (ic.DeleteOnUse) { remove = true; }
@@ -3219,9 +3238,17 @@ namespace Barotrauma
item.RecalculateConditionValues();
item.SetActiveSprite();
if (submarine?.Info.GameVersion != null)
Version savedVersion = submarine?.Info.GameVersion;
if (element.Document?.Root != null && element.Document.Root.Name.ToString().Equals("gamesession", StringComparison.OrdinalIgnoreCase))
{
SerializableProperty.UpgradeGameVersion(item, item.Prefab.ConfigElement, submarine.Info.GameVersion);
//character inventories are loaded from the game session file - use the version number of the saved game session instead of the sub
//(the sub may have already been saved and up-to-date, even though the character inventories aren't)
savedVersion = new Version(element.Document.Root.GetAttributeString("version", "0.0.0.0"));
}
if (savedVersion != null)
{
SerializableProperty.UpgradeGameVersion(item, item.Prefab.ConfigElement, savedVersion);
}
foreach (ItemComponent component in item.components)
@@ -3342,6 +3369,7 @@ namespace Barotrauma
ic.ShallowRemove();
}
ItemList.Remove(this);
dangerousItems.Remove(this);
if (body != null)
{
@@ -3400,6 +3428,7 @@ namespace Barotrauma
#endif
}
ItemList.Remove(this);
dangerousItems.Remove(this);
if (body != null)
{
@@ -26,9 +26,15 @@ namespace Barotrauma
public readonly ImmutableArray<StartItem> Items;
/// <summary>
/// The order in which the sets are displayed in menus
/// </summary>
public readonly int Order;
public StartItemSet(ContentXElement element, StartItemsFile file) : base(file, element.GetAttributeIdentifier("identifier", Identifier.Empty))
{
Items = element.Elements().Select(e => new StartItem(e!)).ToImmutableArray();
Order = element.GetAttributeInt("order", 0);
}
public override void Dispose() { }