Unstable 0.16.0.0

This commit is contained in:
Markus Isberg
2022-01-14 01:28:24 +09:00
parent d9baeaa2e1
commit 7d6421a548
237 changed files with 6430 additions and 2205 deletions
@@ -144,16 +144,6 @@ namespace Barotrauma.Items.Components
if (linkedGap == null)
{
Rectangle rect = item.Rect;
if (IsHorizontal)
{
rect.Y += 5;
rect.Height += 10;
}
else
{
rect.X -= 5;
rect.Width += 10;
}
linkedGap = new Gap(rect, !IsHorizontal, Item.Submarine)
{
Submarine = item.Submarine
@@ -55,17 +55,22 @@ namespace Barotrauma.Items.Components
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
public Vector2 SpawnAmountRange { get; set; }
[Editable(MinValueInt = int.MinValue, MaxValueInt = int.MaxValue), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, true, "Total maximum amount of items or creatures that can be spawned. 0 = unrestricted.")]
public int MaximumAmount { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned. 0 = unrestricted.")]
public int MaximumAmountInArea { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
public float MaximumAmountRangePadding { get; set; }
[Serialize(true, true, "")]
public bool CanSpawn { get; set; } = true;
private float SpawnTimer;
private float? SpawnTimerGoal;
private float spawnTimer;
private float? spawnTimerGoal;
private int spawnedAmount = 0;
public EntitySpawnerComponent(Item item, XElement element) : base(item, element)
{
@@ -115,15 +120,15 @@ namespace Barotrauma.Items.Components
if (minTime < 0 && maxTime < 0) { return; }
SpawnTimerGoal ??= Rand.Range(minTime, maxTime, Rand.RandSync.Unsynced);
spawnTimerGoal ??= Rand.Range(minTime, maxTime, Rand.RandSync.Unsynced);
SpawnTimer += deltaTime;
spawnTimer += deltaTime;
if (SpawnTimer > SpawnTimerGoal)
if (spawnTimer > spawnTimerGoal)
{
Spawn();
SpawnTimerGoal = null;
SpawnTimer = 0;
spawnTimerGoal = null;
spawnTimer = 0;
}
}
@@ -149,12 +154,12 @@ namespace Barotrauma.Items.Components
private RectangleF GetAreaRectangle(Vector2 size, Vector2 offset, bool draw)
{
Vector2 pos = item.WorldPosition;
pos += offset;
if (draw)
{
pos.Y = -pos.Y;
}
pos += offset;
RectangleF rect = new RectangleF(pos.X - size.X / 2f, pos.Y - size.Y / 2f, size.X, size.Y);
return rect;
}
@@ -162,6 +167,7 @@ namespace Barotrauma.Items.Components
private bool CanSpawnMore()
{
if (!CanSpawn) { return false; }
if (MaximumAmount > 0 && spawnedAmount >= MaximumAmount) { return false; }
if (OnlySpawnWhenCrewInRange)
{
@@ -171,10 +177,9 @@ namespace Barotrauma.Items.Components
}
}
if (MaximumAmount < 0) { return true; }
if (MaximumAmountInArea <= 0) { return true; }
int amount;
if (!string.IsNullOrWhiteSpace(SpeciesName))
{
amount = Character.CharacterList.Count(c => !c.IsDead && c.SpeciesName.Equals(SpeciesName, StringComparison.OrdinalIgnoreCase) && IsInRange(c.WorldPosition, crewArea: false, rangePad: true));
@@ -188,13 +193,12 @@ namespace Barotrauma.Items.Components
return false;
}
return amount < MaximumAmount;
return amount < MaximumAmountInArea;
}
private bool IsInRange(Vector2 worldPos, bool crewArea = false, bool rangePad = false)
{
Vector2 offset = crewArea ? CrewAreaOffset : SpawnAreaOffset;
offset.Y = -offset.Y;
switch (crewArea ? CrewAreaShape : SpawnAreaShape)
{
case AreaShape.Circle:
@@ -269,6 +273,7 @@ namespace Barotrauma.Items.Components
string[] allSpecies = SpeciesName.Split(',');
string species = allSpecies.GetRandom().Trim();
Entity.Spawner?.AddToSpawnQueue(species, pos);
spawnedAmount++;
}
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
{
@@ -283,6 +288,7 @@ namespace Barotrauma.Items.Components
}
Entity.Spawner?.AddToSpawnQueue(prefab, pos, item.Submarine);
spawnedAmount++;
}
}
}
@@ -74,6 +74,7 @@ namespace Barotrauma.Items.Components
a.Identifier.Equals(Effect, StringComparison.OrdinalIgnoreCase) ||
a.AfflictionType.Equals(Effect, StringComparison.OrdinalIgnoreCase)).GetRandom();
}
Tainted = true;
}
[Serialize(3.0f, false)]
@@ -94,37 +95,28 @@ namespace Barotrauma.Items.Components
if (targetCharacter != null) { return; }
if (tainted)
{
if (selectedTaintedEffect != null)
{
float selectedTaintedEffectStrength = item.ConditionPercentage / 100.0f * selectedTaintedEffect.MaxStrength;
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
var existingAffliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (existingAffliction != null)
{
existingAffliction.Strength = selectedTaintedEffectStrength;
}
targetCharacter = character;
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
if (selectedEffect != null)
{
ApplyStatusEffects(ActionType.OnWearing, 1.0f);
float selectedEffectStrength = item.ConditionPercentage / 100.0f * selectedEffect.MaxStrength;
character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
var existingAffliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (existingAffliction != null)
{
existingAffliction.Strength = selectedEffectStrength;
}
targetCharacter = character;
ApplyStatusEffects(ActionType.OnWearing, 1.0f);
float selectedEffectStrength = GetCombinedEffectStrength();
character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (affliction != null) { affliction.Strength = selectedEffectStrength; }
#if SERVER
item.CreateServerEvent(this);
#endif
}
if (tainted && selectedTaintedEffect != null)
{
float selectedTaintedEffectStrength = GetCombinedTaintedEffectStrength();
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (affliction != null) { affliction.Strength = selectedTaintedEffectStrength; }
targetCharacter = character;
#if SERVER
item.CreateServerEvent(this);
#endif
}
foreach (Item containedItem in item.ContainedItems)
{
@@ -142,13 +134,14 @@ namespace Barotrauma.Items.Components
(rootContainer == null || !targetCharacter.HasEquippedItem(rootContainer) || !targetCharacter.Inventory.IsInLimbSlot(rootContainer, InvSlotType.HealthInterface)))
{
item.ApplyStatusEffects(ActionType.OnSevered, 1.0f, targetCharacter);
targetCharacter.CharacterHealth.ReduceAffliction(null, selectedEffect.Identifier, selectedEffect.MaxStrength);
if (tainted)
{
targetCharacter.CharacterHealth.ReduceAffliction(null, selectedTaintedEffect.Identifier, selectedTaintedEffect.MaxStrength);
}
targetCharacter = null;
IsActive = false;
var affliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (affliction != null) { affliction.Strength = GetCombinedEffectStrength(); }
var taintedAffliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (taintedAffliction != null) { taintedAffliction.Strength = GetCombinedTaintedEffectStrength(); }
targetCharacter = null;
}
}
}
@@ -184,6 +177,36 @@ namespace Barotrauma.Items.Components
}
}
private float GetCombinedEffectStrength()
{
float effectStrength = 0.0f;
foreach (Item otherItem in targetCharacter.Inventory.FindAllItems(recursive: true))
{
var geneticMaterial = otherItem.GetComponent<GeneticMaterial>();
if (geneticMaterial == null || !geneticMaterial.IsActive) { continue; }
if (geneticMaterial.selectedEffect == selectedEffect)
{
effectStrength += otherItem.ConditionPercentage / 100.0f * selectedEffect.MaxStrength;
}
}
return effectStrength;
}
private float GetCombinedTaintedEffectStrength()
{
float taintedEffectStrength = 0.0f;
foreach (Item otherItem in targetCharacter.Inventory.FindAllItems(recursive: true))
{
var geneticMaterial = otherItem.GetComponent<GeneticMaterial>();
if (geneticMaterial == null || !geneticMaterial.IsActive) { continue; }
if (selectedTaintedEffect != null && geneticMaterial.selectedTaintedEffect == selectedTaintedEffect)
{
taintedEffectStrength += otherItem.ConditionPercentage / 100.0f * selectedTaintedEffect.MaxStrength;
}
}
return taintedEffectStrength;
}
private float GetTaintedProbabilityOnRefine(Character user)
{
if (user == null) { return 1.0f; }
@@ -278,7 +278,7 @@ namespace Barotrauma.Items.Components
for (int i = 0, j = 0; i < maxSides; i++)
{
if (!occupiedSides.IsBitSet((TileSide) (1 << i)))
if (!occupiedSides.HasFlag((TileSide) (1 << i)))
{
pool[j] = i;
j++;
@@ -303,7 +303,7 @@ namespace Barotrauma.Items.Components
public bool CanGrowMore() => (Sides | BlockedSides).Count() < 4;
public bool IsSideBlocked(TileSide side) => BlockedSides.IsBitSet(side) || Sides.IsBitSet(side);
public bool IsSideBlocked(TileSide side) => BlockedSides.HasFlag(side) || Sides.HasFlag(side);
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int) pos.X - Size / 2, (int) pos.Y + Size / 2, Size, Size);
}
@@ -774,7 +774,7 @@ namespace Barotrauma.Items.Components
TileSide oppositeSide = connectingSide.GetOppositeSide();
if (otherVine.BlockedSides.IsBitSet(connectingSide))
if (otherVine.BlockedSides.HasFlag(connectingSide))
{
newVine.BlockedSides |= oppositeSide;
continue;
@@ -166,7 +166,7 @@ namespace Barotrauma.Items.Components
Pusher = new PhysicsBody(item.body.width, item.body.height, item.body.radius, item.body.Density)
{
BodyType = BodyType.Dynamic,
CollidesWith = Physics.CollisionCharacter,
CollidesWith = Physics.CollisionCharacter | Physics.CollisionProjectile,
CollisionCategories = Physics.CollisionItemBlocking,
Enabled = false,
UserData = this
@@ -604,7 +604,14 @@ namespace Barotrauma.Items.Components
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
int currentlyAttachedCount = Item.ItemList.Count(
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
if (currentlyAttachedCount >= maxAttachableCount)
if (maxAttachableCount == 0)
{
#if CLIENT
GUI.AddMessage(TextManager.Get("itemmsgrequiretraining"), Color.Red);
#endif
return false;
}
else if (currentlyAttachedCount >= maxAttachableCount)
{
#if CLIENT
GUI.AddMessage($"{TextManager.Get("itemmsgtotalnumberlimited")} ({currentlyAttachedCount}/{maxAttachableCount})", Color.Red);
@@ -801,7 +808,7 @@ namespace Barotrauma.Items.Components
equipLimb = picker.AnimController.GetLimb(LimbType.Torso);
}
if (equipLimb != null)
if (equipLimb != null && !equipLimb.Removed)
{
float itemAngle = (equipLimb.Rotation + holdAngle * picker.AnimController.Dir);
@@ -814,6 +821,11 @@ namespace Barotrauma.Items.Components
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
//do nothing
}
public override void FlipX(bool relativeToSub)
{
handlePos[0].X = -handlePos[0].X;
@@ -115,7 +115,7 @@ namespace Barotrauma.Items.Components
reloadTimer /= (1f + item.GetQualityModifier(Quality.StatType.StrikingSpeedMultiplier));
item.body.FarseerBody.CollisionCategories = Physics.CollisionProjectile;
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall;
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionItemBlocking;
item.body.FarseerBody.OnCollision += OnCollision;
item.body.FarseerBody.IsBullet = true;
item.body.PhysEnabled = true;
@@ -361,6 +361,10 @@ namespace Barotrauma.Items.Components
}
hitTargets.Add(targetItem);
}
else if (f2.Body.UserData is Holdable holdable && holdable.CanPush)
{
hitTargets.Add(holdable.Item);
}
else
{
return false;
@@ -411,6 +415,14 @@ namespace Barotrauma.Items.Components
if (targetItem.Removed) { return; }
Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
}
else if (target.UserData is Holdable holdable && holdable.CanPush)
{
if (holdable.Item.Removed) { return; }
Attack.DoDamage(User, holdable.Item, item.WorldPosition, 1.0f);
RestoreCollision();
hitting = false;
User = null;
}
else
{
return;
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
if (PickingTime > 0.0f)
{
var abilityPickingTime = new AbilityValueItem(PickingTime, item.Prefab);
var abilityPickingTime = new AbilityItemPickingTime(PickingTime, item.Prefab);
picker.CheckTalents(AbilityEffectType.OnItemPicked, abilityPickingTime);
if (requiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
@@ -300,4 +300,15 @@ namespace Barotrauma.Items.Components
}
}
}
class AbilityItemPickingTime : AbilityObject, IAbilityValue, IAbilityItemPrefab
{
public AbilityItemPickingTime(float pickingTime, ItemPrefab itemPrefab)
{
Value = pickingTime;
ItemPrefab = itemPrefab;
}
public float Value { get; set; }
public ItemPrefab ItemPrefab { get; set; }
}
}
@@ -158,7 +158,7 @@ namespace Barotrauma.Items.Components
return MathHelper.ToRadians(spread);
}
private readonly List<Body> limbBodies = new List<Body>();
private readonly List<Body> ignoredBodies = new List<Body>();
public override bool Use(float deltaTime, Character character = null)
{
tryingToCharge = true;
@@ -172,8 +172,8 @@ namespace Barotrauma.Items.Components
if (character != null)
{
var abilityItem = new AbilityItem(item);
character.CheckTalents(AbilityEffectType.OnUseRangedWeapon, abilityItem);
var abilityRangedWeapon = new AbilityRangedWeapon(item);
character.CheckTalents(AbilityEffectType.OnUseRangedWeapon, abilityRangedWeapon);
}
if (item.AiTarget != null)
@@ -182,11 +182,20 @@ namespace Barotrauma.Items.Components
item.AiTarget.SightRange = item.AiTarget.MaxSightRange;
}
limbBodies.Clear();
ignoredBodies.Clear();
foreach (Limb l in character.AnimController.Limbs)
{
if (l.IsSevered) { continue; }
limbBodies.Add(l.body.FarseerBody);
ignoredBodies.Add(l.body.FarseerBody);
}
foreach (Item heldItem in character.HeldItems)
{
var holdable = heldItem.GetComponent<Holdable>();
if (holdable?.Pusher != null)
{
ignoredBodies.Add(holdable.Pusher.FarseerBody);
}
}
float degreeOfFailure = 1.0f - DegreeOfSuccess(character);
@@ -211,7 +220,7 @@ namespace Barotrauma.Items.Components
}
float damageMultiplier = 1f + item.GetQualityModifier(Quality.StatType.FirepowerMultiplier);
projectile.Launcher = item;
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: limbBodies.ToList(), createNetworkEvent: false, damageMultiplier);
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: ignoredBodies.ToList(), createNetworkEvent: false, damageMultiplier);
projectile.Item.GetComponent<Rope>()?.Attach(Item, projectile.Item);
if (i == 0)
{
@@ -270,4 +279,12 @@ namespace Barotrauma.Items.Components
partial void LaunchProjSpecific();
}
class AbilityRangedWeapon : AbilityObject, IAbilityItem
{
public AbilityRangedWeapon(Item item)
{
Item = item;
}
public Item Item { get; set; }
}
}
@@ -521,7 +521,7 @@ namespace Barotrauma.Items.Components
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) { return true; }
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new ISerializableEntity[] { targetStructure });
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, structure: targetStructure);
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
float structureFixAmount = StructureFixAmount;
@@ -589,8 +589,7 @@ namespace Barotrauma.Items.Components
closestLimb.body.ApplyForce(dir * TargetForce, maxVelocity: 10.0f);
}
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse,
closestLimb == null ? new ISerializableEntity[] { targetCharacter } : new ISerializableEntity[] { targetCharacter, closestLimb });
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, character: targetCharacter, limb: closestLimb);
FixCharacterProjSpecific(user, deltaTime, targetCharacter);
return true;
}
@@ -606,7 +605,7 @@ namespace Barotrauma.Items.Components
}
targetLimb.character.LastDamageSource = item;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new ISerializableEntity[] { targetLimb.character, targetLimb });
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, character: targetLimb.character, limb: targetLimb);
FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
return true;
}
@@ -645,7 +644,7 @@ namespace Barotrauma.Items.Components
targetItem.IsHighlighted = true;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, targetItem.AllPropertyObjects);
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, targetItem);
if (targetItem.body != null && !MathUtils.NearlyEqual(TargetForce, 0.0f))
{
@@ -682,7 +681,7 @@ namespace Barotrauma.Items.Components
Reset();
return true;
}
if (leak.Submarine == null)
if (leak.Submarine == null || leak.Submarine != character.Submarine)
{
Reset();
return true;
@@ -836,32 +835,44 @@ namespace Barotrauma.Items.Components
}
}
private void ApplyStatusEffectsOnTarget(Character user, float deltaTime, ActionType actionType, IEnumerable<ISerializableEntity> targets)
private static List<ISerializableEntity> currentTargets = new List<ISerializableEntity>();
private void ApplyStatusEffectsOnTarget(Character user, float deltaTime, ActionType actionType, Item targetItem = null, Character character = null, Limb limb = null, Structure structure = null)
{
if (statusEffectLists == null) { return; }
if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) { return; }
currentTargets.Clear();
foreach (StatusEffect effect in statusEffects)
{
effect.SetUser(user);
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
{
effect.Apply(actionType, deltaTime, item, targets);
if (targetItem != null)
{
currentTargets.AddRange(targetItem.AllPropertyObjects);
}
if (structure != null)
{
currentTargets.Add(structure);
}
effect.Apply(actionType, deltaTime, item, currentTargets);
}
else if (effect.HasTargetType(StatusEffect.TargetType.Character))
{
effect.Apply(actionType, deltaTime, item, targets.Where(t => t is Character));
currentTargets.Add(character);
effect.Apply(actionType, deltaTime, item, currentTargets);
}
else if (effect.HasTargetType(StatusEffect.TargetType.Limb))
{
effect.Apply(actionType, deltaTime, item, targets.Where(t => t is Limb));
currentTargets.Add(limb);
effect.Apply(actionType, deltaTime, item, currentTargets);
}
#if CLIENT
if (user == null) { return; }
// Hard-coded progress bars for welding doors stuck.
// A general purpose system could be better, but it would most likely require changes in the way we define the status effects in xml.
foreach (ISerializableEntity target in targets)
foreach (ISerializableEntity target in currentTargets)
{
if (!(target is Door door)) { continue; }
if (!door.CanBeWelded || !door.Item.IsInteractable(user)) { continue; }
@@ -284,6 +284,43 @@ namespace Barotrauma.Items.Components
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ParseMsg();
string inheritRequiredSkillsFrom = element.GetAttributeString("inheritrequiredskillsfrom", "");
if (!string.IsNullOrEmpty(inheritRequiredSkillsFrom))
{
var component = item.Components.Find(ic => ic.Name.Equals(inheritRequiredSkillsFrom, StringComparison.OrdinalIgnoreCase));
if (component == null)
{
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - component \"{name}\" is set to inherit its required skills from \"{inheritRequiredSkillsFrom}\", but a component of that type couldn't be found.");
}
else
{
requiredSkills = component.requiredSkills;
}
}
string inheritStatusEffectsFrom = element.GetAttributeString("inheritstatuseffectsfrom", "");
if (!string.IsNullOrEmpty(inheritStatusEffectsFrom))
{
var component = item.Components.Find(ic => ic.Name.Equals(inheritStatusEffectsFrom, StringComparison.OrdinalIgnoreCase));
if (component == null)
{
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - component \"{name}\" is set to inherit its StatusEffects from \"{inheritStatusEffectsFrom}\", but a component of that type couldn't be found.");
}
else if (component.statusEffectLists != null)
{
statusEffectLists ??= new Dictionary<ActionType, List<StatusEffect>>();
foreach (KeyValuePair<ActionType, List<StatusEffect>> kvp in component.statusEffectLists)
{
if (!statusEffectLists.TryGetValue(kvp.Key, out List<StatusEffect> effectList))
{
effectList = new List<StatusEffect>();
statusEffectLists.Add(kvp.Key, effectList);
}
effectList.AddRange(kvp.Value);
}
}
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -315,19 +352,8 @@ namespace Barotrauma.Items.Components
requiredSkills.Add(new Skill(skillIdentifier, subElement.GetAttributeInt("level", 0)));
break;
case "statuseffect":
var statusEffect = StatusEffect.Load(subElement, item.Name);
if (statusEffectLists == null) statusEffectLists = new Dictionary<ActionType, List<StatusEffect>>();
List<StatusEffect> effectList;
if (!statusEffectLists.TryGetValue(statusEffect.type, out effectList))
{
effectList = new List<StatusEffect>();
statusEffectLists.Add(statusEffect.type, effectList);
}
effectList.Add(statusEffect);
statusEffectLists ??= new Dictionary<ActionType, List<StatusEffect>>();
LoadStatusEffect(subElement);
break;
default:
if (LoadElemProjSpecific(subElement)) { break; }
@@ -342,6 +368,17 @@ namespace Barotrauma.Items.Components
break;
}
}
void LoadStatusEffect(XElement subElement)
{
var statusEffect = StatusEffect.Load(subElement, item.Name);
if (!statusEffectLists.TryGetValue(statusEffect.type, out List<StatusEffect> effectList))
{
effectList = new List<StatusEffect>();
statusEffectLists.Add(statusEffect.type, effectList);
}
effectList.Add(statusEffect);
}
}
private void SetActiveState(bool isActive)
@@ -399,6 +436,8 @@ namespace Barotrauma.Items.Components
return false;
}
public virtual bool UpdateWhenInactive => false;
//called when isActive is true and condition > 0.0f
public virtual void Update(float deltaTime, Camera cam)
{
@@ -798,7 +837,10 @@ namespace Barotrauma.Items.Components
foreach (ItemComponent ic in item.Components)
{
if (ic.statusEffectLists == null || !ic.statusEffectLists.TryGetValue(ActionType.OnBroken, out List<StatusEffect> brokenEffects)) { continue; }
brokenEffects.ForEach(e => e.SetUser(user));
foreach (var brokenEffect in brokenEffects)
{
brokenEffect.SetUser(user);
}
}
}
@@ -1007,7 +1049,8 @@ namespace Barotrauma.Items.Components
return 0.0f;
}
}
return 1.0f;
// Prefer items with the same identifier as the contained items'
return container.ContainsItemsWithSameIdentifier(i) ? 1.0f : 0.5f;
}
};
containObjective.Abandoned += () => aiController.IgnoredItems.Add(container.Item);
@@ -208,12 +208,13 @@ namespace Barotrauma.Items.Components
public override bool RecreateGUIOnResolutionChange => true;
public List<RelatedItem> ContainableItems { get; }
public ItemContainer(Item item, XElement element)
: base(item, element)
{
int totalCapacity = capacity;
List<RelatedItem> containableItems = null;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -225,8 +226,8 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
continue;
}
containableItems ??= new List<RelatedItem>();
containableItems.Add(containable);
ContainableItems ??= new List<RelatedItem>();
ContainableItems.Add(containable);
break;
case "subcontainer":
totalCapacity += subElement.GetAttributeInt("capacity", 1);
@@ -237,7 +238,7 @@ namespace Barotrauma.Items.Components
slotRestrictions = new SlotRestrictions[totalCapacity];
for (int i = 0; i < capacity; i++)
{
slotRestrictions[i] = new SlotRestrictions(maxStackSize, containableItems);
slotRestrictions[i] = new SlotRestrictions(maxStackSize, ContainableItems);
}
int subContainerIndex = capacity;
@@ -344,6 +345,19 @@ namespace Barotrauma.Items.Components
return slotRestrictions[index].MatchesItem(itemPrefab);
}
public bool ContainsItemsWithSameIdentifier(Item item)
{
if (item == null) { return false; }
foreach (var containedItem in Inventory.AllItems)
{
if (containedItem.Prefab.Identifier == item.Prefab.Identifier)
{
return true;
}
}
return false;
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public override void Update(float deltaTime, Camera cam)
@@ -432,7 +446,7 @@ namespace Barotrauma.Items.Components
}
}
}
var abilityItem = new AbilityItem(item);
var abilityItem = new AbilityItemContainer(item);
character.CheckTalents(AbilityEffectType.OnOpenItemContainer, abilityItem);
return base.Select(character);
@@ -494,6 +508,21 @@ namespace Barotrauma.Items.Components
IsActive = true;
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
switch (connection.Name)
{
case "activate":
case "use":
case "trigger_in":
if (signal.value != "0")
{
item.Use(1.0f, signal.sender);
}
break;
}
}
public void SetContainedItemPositions()
{
Vector2 transformedItemPos = ItemPos * item.Scale;
@@ -689,7 +718,6 @@ namespace Barotrauma.Items.Components
}
}
protected override void ShallowRemoveComponentSpecific()
{
}
@@ -743,4 +771,13 @@ namespace Barotrauma.Items.Components
return componentElement;
}
}
class AbilityItemContainer : AbilityObject, IAbilityItem
{
public AbilityItemContainer(Item item)
{
Item = item;
}
public Item Item { get; set; }
}
}
@@ -370,18 +370,29 @@ namespace Barotrauma.Items.Components
public Item GetFocusTarget()
{
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), "position_out");
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
Item focusTarget = null;
for (int c = 0; c < 2; c++)
{
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
//try finding the item to focus on using trigger_out, and if that fails, using position_out
string connectionName = c == 0 ? "trigger_out" : "position_out";
string signal = c == 0 ? "0" : MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture);
if (!item.SendSignal(new Signal(signal, sender: user), connectionName) || focusTarget != null)
{
return item.LastSentSignalRecipients[i].Item;
continue;
}
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
{
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
{
focusTarget = item.LastSentSignalRecipients[i].Item;
break;
}
}
}
return null;
return focusTarget;
}
public override bool Pick(Character picker)
@@ -170,7 +170,7 @@ namespace Barotrauma.Items.Components
character.CheckTalents(AbilityEffectType.OnItemDeconstructedByAlly, abilityTargetItem);
}
var itemCreationMultiplier = new AbilityValueItem(amountMultiplier, targetItem.Prefab);
var itemCreationMultiplier = new AbilityItemCreationMultiplier(targetItem.Prefab, amountMultiplier);
user.CheckTalents(AbilityEffectType.OnItemDeconstructedMaterial, itemCreationMultiplier);
amountMultiplier = (int)itemCreationMultiplier.Value;
}
@@ -261,8 +261,8 @@ namespace Barotrauma.Items.Components
if (user != null && !user.Removed)
{
// used to spawn items directly into the deconstructor
var itemContainer = new AbilityItemPrefabItem(item, targetItem.Prefab);
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemContainer);
var itemDeconstructedInventory = new AbilityItemDeconstructedInventory(targetItem.Prefab, item);
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemDeconstructedInventory);
}
int amount = (int)amountMultiplier;
@@ -333,7 +333,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < outputContainer.Capacity; i++)
{
var containedItem = outputContainer.Inventory.GetItemAt(i);
if (containedItem?.OwnInventory != null && containedItem.OwnInventory.TryPutItem(item, user: null))
if (containedItem?.OwnInventory != null && containedItem.GetComponent<GeneticMaterial>() == null && containedItem.OwnInventory.TryPutItem(item, user: null))
{
return;
}
@@ -454,4 +454,26 @@ namespace Barotrauma.Items.Components
public Character Character { get; set; }
}
class AbilityItemCreationMultiplier : AbilityObject, IAbilityValue, IAbilityItemPrefab
{
public AbilityItemCreationMultiplier(ItemPrefab itemPrefab, float itemAmountMultiplier)
{
ItemPrefab = itemPrefab;
Value = itemAmountMultiplier;
}
public ItemPrefab ItemPrefab { get; set; }
public float Value { get; set; }
}
class AbilityItemDeconstructedInventory : AbilityObject, IAbilityItem, IAbilityItemPrefab
{
public AbilityItemDeconstructedInventory(ItemPrefab itemPrefab, Item item)
{
ItemPrefab = itemPrefab;
Item = item;
}
public ItemPrefab ItemPrefab { get; set; }
public Item Item { get; set; }
}
}
@@ -179,8 +179,6 @@ namespace Barotrauma.Items.Components
if (selectedItem == null) { return; }
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition * selectedItem.TargetItem.Health)) { return; }
RefreshAvailableIngredients();
#if CLIENT
itemList.Enabled = false;
activateButton.Text = TextManager.Get("FabricatorCancel");
@@ -189,7 +187,13 @@ namespace Barotrauma.Items.Components
IsActive = true;
this.user = user;
fabricatedItem = selectedItem;
MoveIngredientsToInputContainer(selectedItem);
RefreshAvailableIngredients();
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
if (!isClient)
{
MoveIngredientsToInputContainer(selectedItem);
}
requiredTime = GetRequiredTime(fabricatedItem, user);
timeUntilReady = requiredTime;
@@ -230,21 +234,19 @@ namespace Barotrauma.Items.Components
}
if (fabricatedItem == null) { return; }
fabricatedItem = null;
#if CLIENT
#if SERVER
if (user != null)
{
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#elif CLIENT
itemList.Enabled = true;
if (activateButton != null)
{
activateButton.Text = TextManager.Get("FabricatorCreate");
}
#endif
#if SERVER
if (user != null)
{
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#endif
fabricatedItem = null;
}
public override void Update(float deltaTime, Camera cam)
@@ -256,15 +258,20 @@ namespace Barotrauma.Items.Components
}
refreshIngredientsTimer -= deltaTime;
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
bool isClient = GameMain.NetworkMember?.IsClient ?? false;
if (!isClient)
{
CancelFabricating();
return;
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
{
CancelFabricating();
return;
}
}
progressState = fabricatedItem == null ? 0.0f : (requiredTime - timeUntilReady) / requiredTime;
if (GameMain.NetworkMember?.IsClient ?? false)
if (isClient)
{
hasPower = State != FabricatorState.Paused;
if (!hasPower)
@@ -365,28 +372,29 @@ namespace Barotrauma.Items.Components
availableItems.Remove(availableItem);
Entity.Spawner.AddToRemoveQueue(availableItem);
inputContainer.Inventory.RemoveItem(availableItem);
break;
}
}
});
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition * fabricatedItem.TargetItem.Health);
var fabricationValueItem = new AbilityValueItem(fabricatedItem.Amount, fabricatedItem.TargetItem);
var fabricationitemAmount = new AbilityFabricationItemAmount(fabricatedItem.TargetItem, fabricatedItem.Amount);
int quality = 0;
if (user?.Info != null)
{
foreach (Character character in Character.GetFriendlyCrew(user))
{
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationValueItem);
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
quality = GetFabricatedItemQuality(fabricatedItem, user);
}
var tempUser = user;
for (int i = 0; i < (int)fabricationValueItem.Value; i++)
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
{
float outCondition = fabricatedItem.OutCondition;
if (i < amountFittingContainer)
@@ -433,7 +441,7 @@ namespace Barotrauma.Items.Components
{
float userSkill = user.GetSkillLevel(skill.Identifier);
float addedSkill = skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f);
var addedSkillValue = new AbilityValueString(addedSkill, skill.Identifier);
var addedSkillValue = new AbilityFabricatorSkillGain(skill.Identifier, addedSkill);
user.CheckTalents(AbilityEffectType.OnItemFabricationSkillGain, addedSkillValue);
user.Info.IncreaseSkillLevel(
@@ -542,6 +550,11 @@ namespace Barotrauma.Items.Components
private void RefreshAvailableIngredients()
{
Character user = this.user;
#if CLIENT
user ??= Character.Controlled;
#endif
List<Item> itemList = new List<Item>();
itemList.AddRange(inputContainer.Inventory.AllItems);
foreach (MapEntity linkedTo in item.linkedTo)
@@ -550,6 +563,10 @@ namespace Barotrauma.Items.Components
{
var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer == null) { continue; }
if (user != null)
{
if (!itemContainer.HasRequiredItems(user, addMessage: false)) { continue; }
}
var deconstructor = linkedItem.GetComponent<Deconstructor>();
if (deconstructor != null)
@@ -568,17 +585,10 @@ namespace Barotrauma.Items.Components
itemList.AddRange(container.Inventory.AllItems);
}
}
#if CLIENT
if (Character.Controlled?.Inventory != null)
{
itemList.AddRange(Character.Controlled.Inventory.AllItems);
}
#else
if (user?.Inventory != null)
{
itemList.AddRange(user.Inventory.AllItems);
}
#endif
availableIngredients.Clear();
foreach (Item item in itemList)
{
@@ -600,8 +610,6 @@ namespace Barotrauma.Items.Components
//required ingredients that are already present in the input container
List<Item> usedItems = new List<Item>();
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
targetItem.RequiredItems.ForEach(requiredItem => {
for (int i = 0; i < requiredItem.Amount; i++)
{
@@ -630,10 +638,11 @@ namespace Barotrauma.Items.Components
if (!inputContainer.Inventory.CanBePut(availablePrefab))
{
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
unneededItem?.Drop(null, createNetworkEvent: !isClient);
unneededItem?.Drop(null);
}
inputContainer.Inventory.TryPutItem(availablePrefab, user: null, createNetworkEvent: !isClient);
inputContainer.Inventory.TryPutItem(availablePrefab, user: null);
}
break;
}
}
});
@@ -684,5 +693,26 @@ namespace Barotrauma.Items.Components
}
savedFabricatedItem = null;
}
class AbilityFabricatorSkillGain : AbilityObject, IAbilityValue, IAbilitySkillIdentifier
{
public AbilityFabricatorSkillGain(string skillIdentifier, float skillAmount)
{
SkillIdentifier = skillIdentifier;
Value = skillAmount;
}
public float Value { get; set; }
public string SkillIdentifier { get; set; }
}
class AbilityFabricationItemAmount : AbilityObject, IAbilityValue, IAbilityItemPrefab
{
public AbilityFabricationItemAmount(ItemPrefab itemPrefab, float itemAmount)
{
ItemPrefab = itemPrefab;
Value = itemAmount;
}
public float Value { get; set; }
public ItemPrefab ItemPrefab { get; set; }
}
}
}
@@ -29,6 +29,15 @@ namespace Barotrauma.Items.Components
}
}
public float CurrentBrokenVolume
{
get
{
if (item.ConditionPercentage > 10.0f || !IsActive) { return 0.0f; }
return (1.0f - item.ConditionPercentage / 10.0f) * 100.0f;
}
}
private float pumpSpeedLockTimer, isActiveLockTimer;
[Serialize(0.0f, true, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
@@ -72,6 +81,8 @@ namespace Barotrauma.Items.Components
private const float TinkeringSpeedIncrease = 4.0f;
public override bool UpdateWhenInactive => true;
public Pump(Item item, XElement element)
: base(item, element)
{
@@ -82,11 +93,15 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
pumpSpeedLockTimer -= deltaTime;
isActiveLockTimer -= deltaTime;
if (!IsActive) { return; }
currFlow = 0.0f;
if (TargetLevel != null)
{
pumpSpeedLockTimer -= deltaTime;
float hullPercentage = 0.0f;
if (item.CurrentHull != null) { hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f; }
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
@@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components
{
if (lastUser == value) { return; }
lastUser = value;
degreeOfSuccess = lastUser == null ? 0.0f : DegreeOfSuccess(lastUser);
degreeOfSuccess = lastUser == null ? 0.0f : Math.Min(DegreeOfSuccess(lastUser), 1.0f);
LastUserWasPlayer = lastUser.IsPlayer;
}
}
@@ -601,7 +601,7 @@ namespace Barotrauma.Items.Components
if (!shutDown)
{
float degreeOfSuccess = DegreeOfSuccess(character);
float degreeOfSuccess = Math.Min(DegreeOfSuccess(character), 1.0f);
float refuelLimit = 0.3f;
//characters with insufficient skill levels don't refuel the reactor
if (degreeOfSuccess > refuelLimit)
@@ -106,6 +106,13 @@ namespace Barotrauma.Items.Components
set;
}
[Editable, Serialize(false, false, description: "Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
public bool CenterOnTransducers
{
get;
set;
}
[Editable, Serialize(false, false, description: "Does the sonar have mineral scanning mode. " +
"Only available in-game when the Item has no Steering component.")]
public bool HasMineralScanner { get; set; }
@@ -318,7 +325,7 @@ namespace Barotrauma.Items.Components
Vector2 transducerPosSum = Vector2.Zero;
foreach (ConnectedTransducer transducer in connectedTransducers)
{
if (transducer.Transducer.Item.Submarine != null)
if (transducer.Transducer.Item.Submarine != null && CenterOnTransducers)
{
return transducer.Transducer.Item.Submarine.WorldPosition;
}
@@ -311,7 +311,7 @@ namespace Barotrauma.Items.Components
}
// override autopilot pathing while the AI rams, and go full speed ahead
if (AIRamTimer > 0f)
if (AIRamTimer > 0f && controlledSub != null)
{
AIRamTimer -= deltaTime;
TargetVelocity = GetSteeringVelocity(AITacticalTarget, 0f);
@@ -1,7 +1,6 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
@@ -111,6 +110,17 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(false, true, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
public bool ExponentialRechargeSpeed { get; set; }
private float efficiency;
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, true, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
public float Efficiency
{
get { return efficiency; }
set { efficiency = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public float RechargeRatio => RechargeSpeed / MaxRechargeSpeed;
public const float aiRechargeTargetRatio = 0.5f;
@@ -170,7 +180,7 @@ namespace Barotrauma.Items.Components
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
}
if (charge >= capacity)
{
//rechargeVoltage = 0.0f;
@@ -181,13 +191,17 @@ namespace Barotrauma.Items.Components
{
float missingCharge = capacity - charge;
float targetRechargeSpeed = rechargeSpeed;
if (ExponentialRechargeSpeed)
{
targetRechargeSpeed = MathF.Pow(rechargeSpeed / maxRechargeSpeed, 2) * maxRechargeSpeed;
}
if (missingCharge < 1.0f)
{
targetRechargeSpeed *= missingCharge;
}
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, targetRechargeSpeed, 0.05f);
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f;
}
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f * efficiency;
}
if (charge <= 0.0f)
{
@@ -10,6 +10,8 @@ namespace Barotrauma.Items.Components
{
public List<Connection> PowerConnections { get; private set; }
private readonly HashSet<Connection> signalConnections = new HashSet<Connection>();
private readonly Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
//a list of connections a given connection is connected to, either directly or via other power transfer components
@@ -121,6 +123,7 @@ namespace Barotrauma.Items.Components
partial void InitProjectSpecific(XElement element);
private static readonly HashSet<PowerTransfer> recipientsToRefresh = new HashSet<PowerTransfer>();
public override void UpdateBroken(float deltaTime, Camera cam)
{
base.UpdateBroken(deltaTime, cam);
@@ -132,7 +135,8 @@ namespace Barotrauma.Items.Components
powerLoad = 0.0f;
currPowerConsumption = 0.0f;
SetAllConnectionsDirty();
foreach (HashSet<Connection> recipientList in connectedRecipients.Values.ToList())
recipientsToRefresh.Clear();
foreach (HashSet<Connection> recipientList in connectedRecipients.Values)
{
foreach (Connection c in recipientList)
{
@@ -140,16 +144,26 @@ namespace Barotrauma.Items.Components
var recipientPowerTransfer = c.Item.GetComponent<PowerTransfer>();
if (recipientPowerTransfer != null)
{
recipientPowerTransfer.SetAllConnectionsDirty();
recipientPowerTransfer.RefreshConnections();
recipientsToRefresh.Add(recipientPowerTransfer);
}
}
}
foreach (PowerTransfer recipientPowerTransfer in recipientsToRefresh)
{
recipientPowerTransfer.SetAllConnectionsDirty();
recipientPowerTransfer.RefreshConnections();
}
RefreshConnections();
isBroken = true;
}
}
private int prevSentPowerValue;
private string powerSignal;
private int prevSentLoadValue;
private string loadSignal;
public override void Update(float deltaTime, Camera cam)
{
RefreshConnections();
@@ -172,6 +186,19 @@ namespace Barotrauma.Items.Components
//if the item can't be fixed, don't allow it to break
if (!item.Repairables.Any() || !CanBeOverloaded) { return; }
if (prevSentPowerValue != (int)-CurrPowerConsumption || powerSignal == null)
{
prevSentPowerValue = (int)Math.Round(-CurrPowerConsumption);
powerSignal = prevSentPowerValue.ToString();
}
if (prevSentLoadValue != (int)powerLoad || loadSignal == null)
{
prevSentLoadValue = (int)Math.Round(powerLoad);
loadSignal = prevSentLoadValue.ToString();
}
item.SendSignal(powerSignal, "power_value_out");
item.SendSignal(loadSignal, "load_value_out");
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
@@ -217,6 +244,7 @@ namespace Barotrauma.Items.Components
return picker != null;
}
private static readonly HashSet<Connection> tempConnected = new HashSet<Connection>();
protected void RefreshConnections()
{
var connections = item.Connections;
@@ -229,15 +257,15 @@ namespace Barotrauma.Items.Components
else if (!connectionDirty[c])
{
continue;
}
}
//find all connections that are connected to this one (directly or via another PowerTransfer)
HashSet<Connection> connected = new HashSet<Connection>();
tempConnected.Clear();
if (item.Condition > 0.0f)
{
if (!connectedRecipients.ContainsKey(c))
{
connectedRecipients.Add(c, connected);
connectedRecipients.Add(c, tempConnected);
}
else
{
@@ -249,24 +277,22 @@ namespace Barotrauma.Items.Components
}
}
connected.Add(c);
GetConnected(c, connected);
tempConnected.Add(c);
GetConnected(c, tempConnected);
}
connectedRecipients[c] = connected;
connectedRecipients[c] = tempConnected;
//go through all the PowerTransfers that we're connected to and set their connections to match the ones we just calculated
//(no need to go through the recursive GetConnected method again)
foreach (Connection recipient in connected)
foreach (Connection recipient in tempConnected)
{
if (recipient == c) { continue; }
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
if (recipientPowerTransfer == null) continue;
if (recipientPowerTransfer == null) { continue; }
if (!connectedRecipients.ContainsKey(recipient))
{
connectedRecipients.Add(recipient, connected);
connectedRecipients.Add(recipient, tempConnected);
}
recipientPowerTransfer.connectedRecipients[recipient] = connected;
recipientPowerTransfer.connectionDirty[recipient] = false;
}
}
@@ -296,7 +322,7 @@ namespace Barotrauma.Items.Components
public void SetAllConnectionsDirty()
{
if (item.Connections == null) return;
if (item.Connections == null) { return; }
foreach (Connection c in item.Connections)
{
connectionDirty[c] = true;
@@ -321,6 +347,14 @@ namespace Barotrauma.Items.Components
return;
}
foreach (Connection c in connections)
{
if (c.Name.Length > 5 && c.Name.Substring(0, 6) == "signal")
{
signalConnections.Add(c);
}
}
if (!(this is RelayComponent))
{
if (PowerConnections.Any(p => !p.IsOutput) && PowerConnections.Any(p => p.IsOutput))
@@ -356,29 +390,30 @@ namespace Barotrauma.Items.Components
{
if (item.Condition <= 0.0f || connection.IsPower) { return; }
if (!connectedRecipients.ContainsKey(connection)) { return; }
if (!signalConnections.Contains(connection)) { return; }
if (connection.Name.Length > 5 && connection.Name.Substring(0, 6) == "signal")
foreach (Connection recipient in connectedRecipients[connection])
{
foreach (Connection recipient in connectedRecipients[connection])
if (recipient.Item == item || recipient.Item == signal.source) { continue; }
signal.source?.LastSentSignalRecipients.Add(recipient);
foreach (ItemComponent ic in recipient.Item.Components)
{
if (recipient.Item == item || recipient.Item == signal.source) { continue; }
signal.source?.LastSentSignalRecipients.Add(recipient);
foreach (ItemComponent ic in recipient.Item.Components)
{
//other junction boxes don't need to receive the signal in the pass-through signal connections
//because we relay it straight to the connected items without going through the whole chain of junction boxes
if (ic is PowerTransfer && !(ic is RelayComponent)) { continue; }
ic.ReceiveSignal(signal, recipient);
}
//other junction boxes don't need to receive the signal in the pass-through signal connections
//because we relay it straight to the connected items without going through the whole chain of junction boxes
if (ic is PowerTransfer && !(ic is RelayComponent)) { continue; }
ic.ReceiveSignal(signal, recipient);
}
if (recipient.Effects != null && signal.value != "0" && !string.IsNullOrEmpty(signal.value))
{
foreach (StatusEffect effect in recipient.Effects)
{
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
}
}
}
}
}
protected override void RemoveComponentSpecific()
@@ -272,7 +272,7 @@ namespace Barotrauma.Items.Components
powered.voltage = -pt1.CurrPowerConsumption / Math.Max(pt1.PowerLoad, 1.0f);
continue;
}
if (powered.powerConsumption <= 0.0f && !(powered is PowerContainer))
if ((powered.powerConsumption <= 0.0f || (powered.Item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && repairable.TinkeringPowersDevices)) && !(powered is PowerContainer))
{
powered.voltage = 1.0f;
continue;
@@ -268,7 +268,8 @@ namespace Barotrauma.Items.Components
IgnoredBodies = ignoredBodies;
Vector2 projectilePos = weaponPos;
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
if (Submarine.PickBody(weaponPos, spawnPos, IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
if (Submarine.PickBody(weaponPos, spawnPos, IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking,
customPredicate: (Fixture f) => { return !IgnoredBodies.Contains(f.Body); }) == null)
{
//no obstacles -> we can spawn the projectile at the barrel
projectilePos = spawnPos;
@@ -359,7 +360,7 @@ namespace Barotrauma.Items.Components
item.body.FarseerBody.IsBullet = true;
item.body.CollisionCategories = Physics.CollisionProjectile;
item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking;
IsActive = true;
@@ -16,6 +16,9 @@ namespace Barotrauma.Items.Components
private float deteriorationTimer;
private float deteriorateAlwaysResetTimer;
private int prevSentConditionValue;
private string conditionSignal;
bool wasBroken;
bool wasGoodCondition;
@@ -113,6 +116,9 @@ namespace Barotrauma.Items.Components
public float TinkeringStrength => tinkeringStrength;
private bool tinkeringPowersDevices;
public bool TinkeringPowersDevices => tinkeringPowersDevices;
public bool IsBelowRepairThreshold => item.ConditionPercentage <= RepairThreshold;
public bool IsBelowRepairIconThreshold => item.ConditionPercentage <= RepairThreshold / 2;
@@ -266,6 +272,7 @@ namespace Barotrauma.Items.Components
if (action == FixActions.Tinker)
{
tinkeringStrength = 1f + CurrentFixer.GetStatValue(StatTypes.TinkeringStrength);
tinkeringPowersDevices = CurrentFixer.HasAbilityFlag(AbilityFlags.TinkeringPowersDevices);
if (character.HasAbilityFlag(AbilityFlags.CanTinkerFabricatorsAndDeconstructors) && item.GetComponent<Deconstructor>() != null || item.GetComponent<Fabricator>() != null)
{
@@ -346,7 +353,13 @@ namespace Barotrauma.Items.Components
UpdateProjSpecific(deltaTime);
IsTinkering = false;
item.SendSignal($"{(int) item.ConditionPercentage}", "condition_out");
if (prevSentConditionValue != (int)item.ConditionPercentage || conditionSignal == null)
{
prevSentConditionValue = (int)item.ConditionPercentage;
conditionSignal = prevSentConditionValue.ToString();
}
item.SendSignal(conditionSignal, "condition_out");
if (CurrentFixer == null)
{
@@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components
//the output is sent if both inputs have received a signal within the timeframe
protected float timeFrame;
protected readonly Character[] signalSender = new Character[2];
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.", alwaysUseInstanceValues: true)]
public float TimeFrame
@@ -80,14 +82,14 @@ namespace Barotrauma.Items.Components
bool sendOutput = true;
for (int i = 0; i < timeSinceReceived.Length; i++)
{
if (timeSinceReceived[i] > timeFrame) sendOutput = false;
if (timeSinceReceived[i] > timeFrame) { sendOutput = false; }
timeSinceReceived[i] += deltaTime;
}
string signalOut = sendOutput ? output : falseOutput;
if (string.IsNullOrEmpty(signalOut)) return;
if (string.IsNullOrEmpty(signalOut)) { return; }
item.SendSignal(signalOut, "signal_out");
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
}
public override void ReceiveSignal(Signal signal, Connection connection)
@@ -95,12 +97,14 @@ namespace Barotrauma.Items.Components
switch (connection.Name)
{
case "signal_in1":
if (signal.value == "0") return;
if (signal.value == "0") { return; }
timeSinceReceived[0] = 0.0f;
signalSender[0] = signal.sender;
break;
case "signal_in2":
if (signal.value == "0") return;
if (signal.value == "0") { return; }
timeSinceReceived[1] = 0.0f;
signalSender[1] = signal.sender;
break;
case "set_output":
output = signal.value;
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
private HashSet<ItemPrefab> ActivatingItemPrefabs { get; set; } = new HashSet<ItemPrefab>();
private bool AllowUsingButtons => ActivatingItemPrefabs.None() || Container.Inventory.AllItems.Any(i => i != null && ActivatingItemPrefabs.Any(p => p == i.Prefab));
private bool AllowUsingButtons => ActivatingItemPrefabs.None() || (Container != null && Container.Inventory.AllItems.Any(i => i != null && ActivatingItemPrefabs.Any(p => p == i.Prefab)));
public ButtonTerminal(Item item, XElement element) : base(item, element)
{
@@ -101,12 +101,12 @@ namespace Barotrauma.Items.Components
partial void OnItemLoadedProjSpecific();
private bool SendSignal(int signalIndex, bool isServerMessage = false)
private bool SendSignal(int signalIndex, Character sender, bool isServerMessage = false)
{
if (!isServerMessage && !AllowUsingButtons) { return false; }
string signal = Signals[signalIndex];
string connectionName = $"signal_out{signalIndex + 1}";
item.SendSignal(signal, connectionName);
item.SendSignal(new Signal(signal, sender: sender), connectionName);
return true;
}
@@ -17,6 +17,12 @@ namespace Barotrauma.Items.Components
}
}
[Editable, Serialize("", false)]
public string Separator
{
get;
set;
}
public ConcatComponent(Item item, XElement element)
: base(item, element)
@@ -25,7 +31,15 @@ namespace Barotrauma.Items.Components
protected override string Calculate(string signal1, string signal2)
{
string output = signal1 + signal2;
string output;
if (string.IsNullOrEmpty(Separator))
{
output = signal1 + signal2;
}
else
{
output = signal1 + Separator + signal2;
}
return output.Length <= maxOutputLength ? output : output.Substring(0, MaxOutputLength);
}
}
@@ -25,7 +25,7 @@ namespace Barotrauma.Items.Components
get { return wires; }
}
private Item item;
private readonly Item item;
public readonly bool IsOutput;
@@ -142,7 +142,6 @@ namespace Barotrauma.Items.Components
IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
Effects = new List<StatusEffect>();
wireId = new ushort[MaxWires];
@@ -164,6 +163,7 @@ namespace Barotrauma.Items.Components
break;
case "statuseffect":
Effects ??= new List<StatusEffect>();
Effects.Add(StatusEffect.Load(subElement, item.Name + ", connection " + Name));
break;
}
@@ -272,7 +272,7 @@ namespace Barotrauma.Items.Components
ic.ReceiveSignal(signal, connection);
}
if (signal.value != "0")
if (recipient.Effects != null && signal.value != "0" && !string.IsNullOrEmpty(signal.value))
{
foreach (StatusEffect effect in recipient.Effects)
{
@@ -24,7 +24,19 @@ namespace Barotrauma.Items.Components
/// </summary>
public bool AlwaysAllowRewiring
{
get { return item.Submarine?.Info.Type == SubmarineType.BeaconStation; }
get
{
if (item.Submarine == null) { return true; }
switch (item.Submarine.Info.Type)
{
case SubmarineType.Wreck:
case SubmarineType.BeaconStation:
case SubmarineType.EnemySubmarine:
case SubmarineType.Ruin:
return true;
}
return false;
}
}
[Editable, Serialize(false, true, description: "Locked connection panels cannot be rewired in-game.", alwaysUseInstanceValues: true)]
@@ -301,7 +301,6 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
UpdateProjSpecific();
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
{
if (!ciElement.ContinuousSignal) { continue; }
@@ -318,8 +317,6 @@ namespace Barotrauma.Items.Components
}
}
partial void UpdateProjSpecific();
public override XElement Save(XElement parentElement)
{
labels = customInterfaceElementList.Select(ci => ci.Label).ToArray();
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
@@ -24,7 +25,7 @@ namespace Barotrauma.Items.Components
private int signalQueueSize;
private int delayTicks;
private readonly Queue<DelayedSignal> signalQueue;
private readonly Queue<DelayedSignal> signalQueue = new Queue<DelayedSignal>();
private DelayedSignal prevQueuedSignal;
@@ -39,6 +40,7 @@ namespace Barotrauma.Items.Components
delay = value;
delayTicks = (int)(delay / Timing.Step);
signalQueueSize = Math.Max(delayTicks, 1) * 2;
signalQueue.Clear();
}
}
@@ -59,7 +61,6 @@ namespace Barotrauma.Items.Components
public DelayComponent(Item item, XElement element)
: base (item, element)
{
signalQueue = new Queue<DelayedSignal>();
IsActive = true;
}
@@ -74,7 +75,7 @@ namespace Barotrauma.Items.Components
{
var signalOut = signalQueue.Peek();
signalOut.SendDuration -= 1;
item.SendSignal(new Signal(signalOut.Signal.value, strength: signalOut.Signal.strength), "signal_out");
item.SendSignal(new Signal(signalOut.Signal.value, sender: signalOut.Signal.sender, strength: signalOut.Signal.strength), "signal_out");
if (signalOut.SendDuration <= 0)
{
signalQueue.Dequeue();
@@ -115,7 +116,7 @@ namespace Barotrauma.Items.Components
signalQueue.Enqueue(prevQueuedSignal);
break;
case "set_delay":
if (float.TryParse(signal.value, out float newDelay))
if (float.TryParse(signal.value, NumberStyles.Any, CultureInfo.InvariantCulture, out float newDelay))
{
newDelay = MathHelper.Clamp(newDelay, 0, 60);
if (signalQueue.Count > 0 && newDelay != Delay)
@@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components
protected string[] receivedSignal;
private readonly Character[] signalSender = new Character[2];
//the output is sent if both inputs have received a signal within the timeframe
protected float timeFrame;
@@ -90,9 +92,8 @@ namespace Barotrauma.Items.Components
if (sendOutput)
{
string signalOut = receivedSignal[0] == receivedSignal[1] ? output : falseOutput;
if (string.IsNullOrEmpty(signalOut)) return;
item.SendSignal(signalOut, "signal_out");
if (string.IsNullOrEmpty(signalOut)) { return; }
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
}
}
@@ -103,10 +104,15 @@ namespace Barotrauma.Items.Components
case "signal_in1":
receivedSignal[0] = signal.value;
timeSinceReceived[0] = 0.0f;
signalSender[0] = signal.sender;
break;
case "signal_in2":
receivedSignal[1] = signal.value;
timeSinceReceived[1] = 0.0f;
signalSender[1] = signal.sender;
break;
case "set_output":
output = signal.value;
break;
}
}
@@ -32,10 +32,22 @@ namespace Barotrauma.Items.Components
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
base.ReceiveSignal(signal, connection);
float.TryParse(receivedSignal[0], NumberStyles.Float, CultureInfo.InvariantCulture, out val1);
float.TryParse(receivedSignal[1], NumberStyles.Float, CultureInfo.InvariantCulture, out val2);
{
//base.ReceiveSignal(signal, connection);
switch (connection.Name)
{
case "signal_in1":
float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out val1);
timeSinceReceived[0] = 0.0f;
break;
case "signal_in2":
float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out val2);
timeSinceReceived[1] = 0.0f;
break;
case "set_output":
output = signal.value;
break;
}
}
}
}
@@ -50,7 +50,7 @@ namespace Barotrauma.Items.Components
set
{
rotation = value;
SetLightSourceTransform();
SetLightSourceTransformProjSpecific();
}
}
@@ -256,7 +256,7 @@ namespace Barotrauma.Items.Components
return;
}
SetLightSourceTransform();
SetLightSourceTransformProjSpecific();
PhysicsBody body = ParentBody ?? item.body;
if (body != null && !body.Enabled)
@@ -338,7 +338,11 @@ namespace Barotrauma.Items.Components
partial void SetLightSourceState(bool enabled, float brightness);
partial void SetLightSourceTransform();
public void SetLightSourceTransform()
{
SetLightSourceTransformProjSpecific();
}
partial void SetLightSourceTransformProjSpecific();
}
}
@@ -74,6 +74,17 @@ namespace Barotrauma.Items.Components
}
}
public Vector2 TransformedDetectOffset
{
get
{
Vector2 transformedDetectOffset = detectOffset;
if (item.FlippedX) { transformedDetectOffset.X = -transformedDetectOffset.X; }
if (item.FlippedY) { transformedDetectOffset.Y = -transformedDetectOffset.Y; }
return transformedDetectOffset;
}
}
[Editable(MinValueFloat = 0.1f, MaxValueFloat = 100.0f, DecimalCount = 2), Serialize(0.1f, true, description: "How often the sensor checks if there's something moving near it. Higher values are better for performance.", alwaysUseInstanceValues: true)]
public float UpdateInterval
{
@@ -184,15 +195,15 @@ namespace Barotrauma.Items.Components
}
}
Vector2 detectPos = item.WorldPosition + detectOffset;
Vector2 detectPos = item.WorldPosition + TransformedDetectOffset;
Rectangle detectRect = new Rectangle((int)(detectPos.X - rangeX), (int)(detectPos.Y - rangeY), (int)(rangeX * 2), (int)(rangeY * 2));
float broadRangeX = Math.Max(rangeX * 2, 500);
float broadRangeY = Math.Max(rangeY * 2, 500);
if (item.CurrentHull == null && item.Submarine != null && Level.Loaded != null &&
if (item.CurrentHull == null && item.Submarine != null &&
(Target == TargetType.Wall || Target == TargetType.Any))
{
if (Math.Abs(item.Submarine.Velocity.X) > MinimumVelocity || Math.Abs(item.Submarine.Velocity.Y) > MinimumVelocity)
if (Level.Loaded != null && (Math.Abs(item.Submarine.Velocity.X) > MinimumVelocity || Math.Abs(item.Submarine.Velocity.Y) > MinimumVelocity))
{
var cells = Level.Loaded.GetCells(item.WorldPosition, 1);
foreach (var cell in cells)
@@ -268,7 +279,7 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.IsSevered) { continue; }
if (limb.LinearVelocity.LengthSquared() <= MinimumVelocity * MinimumVelocity) { continue; }
if (limb.LinearVelocity.LengthSquared() < MinimumVelocity * MinimumVelocity) { continue; }
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
{
MotionDetected = true;
@@ -276,23 +287,12 @@ namespace Barotrauma.Items.Components
}
}
}
}
}
}
public override void FlipX(bool relativeToSub)
{
detectOffset.X = -detectOffset.X;
}
public override void FlipY(bool relativeToSub)
{
detectOffset.Y = -detectOffset.Y;
}
public override XElement Save(XElement parentElement)
{
Vector2 prevDetectOffset = detectOffset;
//undo flipping before saving
if (item.FlippedX) { detectOffset.X = -detectOffset.X; }
if (item.FlippedY) { detectOffset.Y = -detectOffset.Y; }
XElement element = base.Save(parentElement);
detectOffset = prevDetectOffset;
return element;
@@ -15,14 +15,14 @@ namespace Barotrauma.Items.Components
bool sendOutput = false;
for (int i = 0; i < timeSinceReceived.Length; i++)
{
if (timeSinceReceived[i] <= timeFrame) sendOutput = true;
if (timeSinceReceived[i] <= timeFrame) { sendOutput = true; }
timeSinceReceived[i] += deltaTime;
}
string signalOut = sendOutput ? output : falseOutput;
if (string.IsNullOrEmpty(signalOut)) return;
if (string.IsNullOrEmpty(signalOut)) { return; }
item.SendSignal(signalOut, "signal_out");
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
}
}
}
@@ -4,6 +4,9 @@ namespace Barotrauma.Items.Components
{
class OxygenDetector : ItemComponent
{
private int prevSentOxygenValue;
private string oxygenSignal;
public OxygenDetector(Item item, XElement element)
: base (item, element)
{
@@ -12,9 +15,15 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (item.CurrentHull == null) return;
if (item.CurrentHull == null) { return; }
item.SendSignal(((int)item.CurrentHull.OxygenPercentage).ToString(), "signal_out");
if (prevSentOxygenValue != (int)item.CurrentHull.OxygenPercentage || oxygenSignal == null)
{
prevSentOxygenValue = (int)item.CurrentHull.OxygenPercentage;
oxygenSignal = prevSentOxygenValue.ToString();
}
item.SendSignal(oxygenSignal, "signal_out");
}
}
@@ -9,6 +9,9 @@ namespace Barotrauma.Items.Components
//how often the detector can switch from state to another
const float StateSwitchInterval = 1.0f;
private int prevSentWaterPercentageValue;
private string waterPercentageSignal;
private bool isInWater;
private float stateSwitchDelay;
@@ -106,7 +109,12 @@ namespace Barotrauma.Items.Components
{
waterPercentage = MathHelper.Clamp((int)Math.Ceiling(item.CurrentHull.WaterPercentage), 0, 100);
}
item.SendSignal(waterPercentage.ToString(), "water_%");
if (prevSentWaterPercentageValue != waterPercentage || waterPercentageSignal == null)
{
prevSentWaterPercentageValue = waterPercentage;
waterPercentageSignal = prevSentWaterPercentageValue.ToString();
}
item.SendSignal(waterPercentageSignal, "water_%");
}
string highPressureOut = (item.CurrentHull == null || item.CurrentHull.LethalPressure > 5.0f) ? "1" : "0";
item.SendSignal(highPressureOut, "high_pressure");
@@ -24,6 +24,7 @@ namespace Barotrauma.Items.Components
private readonly int[] channelMemory = new int[ChannelMemorySize];
private Connection signalInConnection;
private Connection signalOutConnection;
[Serialize(CharacterTeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.", alwaysUseInstanceValues: true)]
@@ -98,6 +99,7 @@ namespace Barotrauma.Items.Components
if (item.Connections != null)
{
signalOutConnection = item.Connections.Find(c => c.Name == "signal_out");
signalInConnection = item.Connections.Find(c => c.Name == "signal_in");
}
if (channelMemory.All(m => m == 0))
{
@@ -207,6 +209,18 @@ namespace Barotrauma.Items.Components
if (wifiComp.signalOutConnection != null)
{
if (signal.source != null && wifiComp.signalInConnection != null)
{
if (signal.source.LastSentSignalRecipients.Contains(wifiComp.signalInConnection))
{
//signal already passed through this wifi component -> stop here to prevent an infinite loop
continue;
}
else
{
signal.source.LastSentSignalRecipients.Add(wifiComp.signalInConnection);
}
}
wifiComp.item.SendSignal(s, wifiComp.signalOutConnection);
}
@@ -503,13 +503,6 @@ namespace Barotrauma.Items.Components
return true;
}
public override void Move(Vector2 amount)
{
#if CLIENT
if (item.IsSelected) MoveNodes(amount);
#endif
}
public List<Vector2> GetNodes()
{
return new List<Vector2>(nodes);
@@ -15,14 +15,14 @@ namespace Barotrauma.Items.Components
int sendOutput = 0;
for (int i = 0; i < timeSinceReceived.Length; i++)
{
if (timeSinceReceived[i] <= timeFrame) sendOutput += 1;
if (timeSinceReceived[i] <= timeFrame) { sendOutput += 1; }
timeSinceReceived[i] += deltaTime;
}
string signalOut = sendOutput == 1 ? output : falseOutput;
if (string.IsNullOrEmpty(signalOut)) return;
if (string.IsNullOrEmpty(signalOut)) { return; }
item.SendSignal(signalOut, "signal_out");
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
}
}
}
@@ -141,8 +141,7 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
triggerers.RemoveWhere(t => t.Removed);
LevelTrigger.RemoveDistantTriggerers(PhysicsBody, triggerers, item.WorldPosition);
LevelTrigger.RemoveInActiveTriggerers(PhysicsBody, triggerers);
if (triggerOnce)
{
@@ -333,6 +333,7 @@ namespace Barotrauma.Items.Components
FindLightComponent();
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
targetRotation = rotation;
UpdateTransformedBarrelPos();
}
@@ -1516,7 +1517,7 @@ namespace Barotrauma.Items.Components
minRotation += MathHelper.TwoPi;
maxRotation += MathHelper.TwoPi;
}
rotation = (minRotation + maxRotation) / 2;
targetRotation = rotation = (minRotation + maxRotation) / 2;
UpdateTransformedBarrelPos();
}
@@ -1537,7 +1538,7 @@ namespace Barotrauma.Items.Components
minRotation += MathHelper.TwoPi;
maxRotation += MathHelper.TwoPi;
}
rotation = (minRotation + maxRotation) / 2;
targetRotation = rotation = (minRotation + maxRotation) / 2;
UpdateTransformedBarrelPos();
}
@@ -1607,6 +1608,7 @@ namespace Barotrauma.Items.Components
{
base.OnItemLoaded();
FindLightComponent();
targetRotation = rotation;
if (!loadedBaseRotation.HasValue)
{
if (item.FlippedX) { FlipX(relativeToSub: false); }