This commit is contained in:
Evil Factory
2022-02-24 14:30:39 -03:00
364 changed files with 10838 additions and 3966 deletions
@@ -74,6 +74,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, false, description: "Should the OnUse StatusEffects trigger when docking (on vanilla docking ports these effects emit particles and play a sound).)")]
public bool ApplyEffectsOnDocking
{
get;
set;
}
[Editable, Serialize(DirectionType.None, false, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
public DirectionType ForceDockingDirection { get; set; }
@@ -261,7 +268,7 @@ namespace Barotrauma.Items.Components
DockingDir = GetDir(DockingTarget);
DockingTarget.DockingDir = -DockingDir;
if (applyEffects)
if (applyEffects && ApplyEffectsOnDocking)
{
ApplyStatusEffects(ActionType.OnUse, 1.0f);
}
@@ -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
@@ -118,6 +118,7 @@ namespace Barotrauma.Items.Components
if (character != null && !CharacterUsable) { return false; }
CurrPowerConsumption = powerConsumption;
Voltage = 0.0f;
charging = true;
timer = Duration;
IsActive = true;
@@ -141,7 +142,7 @@ namespace Barotrauma.Items.Components
timer -= deltaTime;
if (charging)
{
if (GetAvailableBatteryPower() >= powerConsumption)
if (GetAvailableInstantaneousBatteryPower() >= powerConsumption)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
float neededPower = powerConsumption;
@@ -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++;
}
}
}
@@ -94,37 +94,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 +133,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 +176,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; }
@@ -1,13 +1,13 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Vector2 = Microsoft.Xna.Framework.Vector2;
using Vector4 = Microsoft.Xna.Framework.Vector4;
@@ -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);
}
@@ -398,8 +398,6 @@ namespace Barotrauma.Items.Components
private int flowerVariants;
private int leafVariants;
private int[] flowerTiles;
private const int serverHealthUpdateDelay = 10;
private int serverHealthUpdateTimer;
public float Health
{
@@ -553,19 +551,21 @@ namespace Barotrauma.Items.Components
if (spawnProduct && ProducedItems.Any())
{
SpawnItem(ProducedItems.RandomElementByWeight(it => it.Probability), spawnPos);
SpawnItem(Item, ProducedItems.RandomElementByWeight(it => it.Probability), spawnPos);
return;
}
if (spawnSeed)
{
SpawnItem(ProducedSeed, spawnPos);
SpawnItem(Item, ProducedSeed, spawnPos);
}
static void SpawnItem(ProducedItem producedItem, Vector2 pos)
static void SpawnItem(Item thisItem, ProducedItem producedItem, Vector2 pos)
{
if (producedItem.Prefab == null) { return; }
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningProduce:" + thisItem.prefab.Identifier + ":" + producedItem.Prefab.Identifier);
Entity.Spawner?.AddToSpawnQueue(producedItem.Prefab, pos, onSpawned: it =>
{
foreach (StatusEffect effect in producedItem.StatusEffects)
@@ -586,8 +586,13 @@ namespace Barotrauma.Items.Components
{
if (Decayed) { return true; }
if (0 >= Health)
if (Health <= 0)
{
if (!Decayed)
{
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningDied:" + item.prefab.Identifier);
}
Decayed = true;
#if CLIENT
foreach (VineTile vine in Vines)
@@ -774,7 +779,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
@@ -237,10 +237,13 @@ namespace Barotrauma.Items.Components
}
}
private bool loadedFromXml;
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
{
base.Load(componentElement, usePrefabValues, idRemap);
loadedFromXml = true;
if (usePrefabValues)
{
//this needs to be loaded regardless
@@ -536,7 +539,16 @@ namespace Barotrauma.Items.Components
else
{
attachTargetCell = GetAttachTargetCell(150.0f);
if (attachTargetCell != null) { IsActive = true; }
if (attachTargetCell != null && attachTargetCell.IsDestructible)
{
attachTargetCell.OnDestroyed += () =>
{
if (attachTargetCell != null && attachTargetCell.CellType != Voronoi2.CellType.Solid)
{
Drop(dropConnectedWires: true, dropper: null);
}
};
}
}
}
@@ -562,7 +574,7 @@ namespace Barotrauma.Items.Components
public void DeattachFromWall()
{
if (!attachable) return;
if (!attachable) { return; }
Attached = false;
attachTargetCell = null;
@@ -604,7 +616,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);
@@ -726,15 +745,6 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (attachTargetCell != null)
{
if (attachTargetCell.CellType != Voronoi2.CellType.Solid)
{
Drop(dropConnectedWires: true, dropper: null);
}
return;
}
if (item.body == null || !item.body.Enabled) { return; }
if (picker == null || !picker.HasEquippedItem(item))
{
@@ -801,7 +811,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 +824,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;
@@ -826,15 +841,25 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
if (item.Submarine != null && item.Submarine.Loading) return;
if (item.Submarine != null && item.Submarine.Loading) { return; }
OnMapLoaded();
item.SetActiveSprite();
}
public override void OnMapLoaded()
{
if (!attachable) return;
if (!attachable) { return; }
//a mod has overridden the item, and the base item didn't have a Holdable component = a mod made the item movable/detachable
if (item.Prefab.IsOverride && !loadedFromXml)
{
if (attachedByDefault)
{
AttachToWall();
return;
}
}
if (Attached)
{
AttachToWall();
@@ -51,7 +51,11 @@ namespace Barotrauma.Items.Components
#else
if (deattachTimer >= DeattachDuration)
{
holdable.DeattachFromWall();
if (holdable.Attached)
{
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + item.Prefab.Identifier);
holdable.DeattachFromWall();
}
trigger.Enabled = false;
}
#endif
@@ -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;
@@ -412,6 +416,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,48 @@ 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; }
foreach (StatusEffect effect in statusEffects)
{
currentTargets.Clear();
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);
}
if (character != null)
{
currentTargets.Add(character);
}
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; }
@@ -286,6 +286,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())
@@ -317,19 +354,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; }
@@ -344,6 +370,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)
@@ -401,6 +438,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)
{
@@ -545,6 +584,7 @@ namespace Barotrauma.Items.Components
{
GUI.RemoveFromUpdateList(GuiFrame, true);
GuiFrame.RectTransform.Parent = null;
GuiFrame = null;
}
#endif
@@ -812,7 +852,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);
}
}
}
@@ -1021,7 +1064,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;
@@ -303,7 +304,12 @@ namespace Barotrauma.Items.Components
}
}
}
}
}
if (item.GetComponent<Planter>() != null)
{
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningPlanted:" + containedItem.prefab.Identifier);
}
//no need to Update() if this item has no statuseffects and no physics body
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
@@ -344,6 +350,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 +451,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 +513,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 +723,6 @@ namespace Barotrauma.Items.Components
}
}
protected override void ShallowRemoveComponentSpecific()
{
}
@@ -743,4 +776,13 @@ namespace Barotrauma.Items.Components
return componentElement;
}
}
class AbilityItemContainer : AbilityObject, IAbilityItem
{
public AbilityItemContainer(Item item)
{
Item = item;
}
public Item Item { get; set; }
}
}
@@ -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;
@@ -300,6 +300,8 @@ namespace Barotrauma.Items.Components
}
}
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + targetItem.prefab.Identifier);
if (targetItem.AllowDeconstruct && allowRemove)
{
//drop all items that are inside the deconstructed item
@@ -333,7 +335,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 +456,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; }
}
}
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
prevVoltage = Voltage;
hasPower = Voltage > MinVoltage;
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f);
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
if (Math.Abs(Force) > 1.0f)
{
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
@@ -137,18 +137,19 @@ namespace Barotrauma.Items.Components
currForce *= MathHelper.Lerp(0.5f, 2.0f, condition);
if (item.Submarine.FlippedX) { currForce *= -1; }
Vector2 forceVector = new Vector2(currForce, 0);
item.Submarine.ApplyForce(forceVector);
item.Submarine.ApplyForce(forceVector * deltaTime * Timing.FixedUpdateRate);
UpdatePropellerDamage(deltaTime);
#if CLIENT
particleTimer -= deltaTime;
if (particleTimer <= 0.0f)
float particleInterval = 1.0f / particlesPerSec;
particleTimer += deltaTime;
while (particleTimer > particleInterval)
{
Vector2 particleVel = -forceVector.ClampLength(5000.0f) / 5.0f;
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos * item.Scale,
particleVel * Rand.Range(0.9f, 1.1f),
particleVel * Rand.Range(0.8f, 1.1f),
0.0f, item.CurrentHull);
particleTimer = 1.0f / particlesPerSec;
}
particleTimer -= particleInterval;
}
#endif
}
}
@@ -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,30 +372,32 @@ 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;
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
if (i < amountFittingContainer)
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
@@ -433,7 +442,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 +551,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 +564,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 +586,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 +611,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 +639,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 +694,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,13 +93,30 @@ 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; }
if (item.CurrentHull != null)
{
float hullWaterVolume = item.CurrentHull.WaterVolume;
float totalHullVolume = item.CurrentHull.Volume;
foreach (var linked in item.CurrentHull.linkedTo)
{
if ((linked is Hull linkedHull))
{
hullWaterVolume += linkedHull.WaterVolume;
totalHullVolume += linkedHull.Volume;
}
}
hullPercentage = hullWaterVolume / totalHullVolume * 100.0f;
}
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
}
@@ -116,8 +144,8 @@ namespace Barotrauma.Items.Components
//less effective when in a bad condition
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
item.CurrentHull.WaterVolume += currFlow;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; }
Voltage -= deltaTime;
}
@@ -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; }
@@ -307,26 +314,6 @@ namespace Barotrauma.Items.Components
return TextManager.GetWithVariable("roomname.subdiroclock", "[dir]", clockDir.ToString());
}
private Vector2 GetTransducerPos()
{
if (!UseTransducers || connectedTransducers.Count == 0)
{
//use the position of the sub if the item is static (no body) and inside a sub
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
}
Vector2 transducerPosSum = Vector2.Zero;
foreach (ConnectedTransducer transducer in connectedTransducers)
{
if (transducer.Transducer.Item.Submarine != null)
{
return transducer.Transducer.Item.Submarine.WorldPosition;
}
transducerPosSum += transducer.Transducer.Item.WorldPosition;
}
return transducerPosSum / connectedTransducers.Count;
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
base.ReceiveSignal(signal, connection);
@@ -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);
@@ -370,8 +370,22 @@ namespace Barotrauma.Items.Components
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.X * Physics.DisplayToRealWorldRatio) * 3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_x");
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.Y * Physics.DisplayToRealWorldRatio) * -3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_y");
item.SendSignal(new Signal((sub.WorldPosition.X * Physics.DisplayToRealWorldRatio).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_y");
Vector2 pos = new Vector2(sub.WorldPosition.X * Physics.DisplayToRealWorldRatio, sub.RealWorldDepth);
if (sonar != null && sonar.UseTransducers && sonar.CenterOnTransducers && sonar.ConnectedTransducers.Any())
{
pos = Vector2.Zero;
foreach (var connectedTransducer in sonar.ConnectedTransducers)
{
pos += connectedTransducer.Item.WorldPosition;
}
pos /= sonar.ConnectedTransducers.Count();
pos = new Vector2(
pos.X * Physics.DisplayToRealWorldRatio,
Level.Loaded?.GetRealWorldDepth(pos.Y) ?? (-pos.Y * Physics.DisplayToRealWorldRatio));
}
item.SendSignal(new Signal(pos.X.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
item.SendSignal(new Signal(pos.Y.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_y");
}
// if our tactical AI pilot has left, revert back to maintaining position
@@ -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,20 @@ 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; }
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, true)]
public float RechargeAdjustSpeed { 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 +183,7 @@ namespace Barotrauma.Items.Components
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
}
if (charge >= capacity)
{
//rechargeVoltage = 0.0f;
@@ -181,13 +194,24 @@ 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;
}
if (currPowerConsumption < targetRechargeSpeed)
{
currPowerConsumption = Math.Min(currPowerConsumption + deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
}
else
{
currPowerConsumption = Math.Max(currPowerConsumption - deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
}
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))
@@ -229,46 +256,54 @@ 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>();
HashSet<Connection> tempConnected;
if (!connectedRecipients.ContainsKey(c))
{
tempConnected = new HashSet<Connection>();
connectedRecipients.Add(c, tempConnected);
}
else
{
tempConnected = connectedRecipients[c];
tempConnected.Clear();
//mark all previous recipients as dirty
foreach (Connection recipient in tempConnected)
{
var pt = recipient.Item.GetComponent<PowerTransfer>();
if (pt != null) { pt.connectionDirty[recipient] = true; }
}
}
tempConnected.Add(c);
if (item.Condition > 0.0f)
{
if (!connectedRecipients.ContainsKey(c))
GetConnected(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 tempConnected)
{
connectedRecipients.Add(c, connected);
}
else
{
//mark all previous recipients as dirty
foreach (Connection recipient in connectedRecipients[c])
if (recipient == c) { continue; }
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
if (recipientPowerTransfer == null) { continue; }
if (!recipientPowerTransfer.connectedRecipients.ContainsKey(recipient))
{
var pt = recipient.Item.GetComponent<PowerTransfer>();
if (pt != null) pt.connectionDirty[recipient] = true;
recipientPowerTransfer.connectedRecipients.Add(recipient, new HashSet<Connection>());
}
else
{
recipientPowerTransfer.connectedRecipients[recipient].Clear();
}
foreach (var connection in tempConnected)
{
recipientPowerTransfer.connectedRecipients[recipient].Add(connection);
}
recipientPowerTransfer.connectionDirty[recipient] = false;
}
connected.Add(c);
GetConnected(c, connected);
}
connectedRecipients[c] = connected;
//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)
{
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
if (recipientPowerTransfer == null) continue;
if (!connectedRecipients.ContainsKey(recipient))
{
connectedRecipients.Add(recipient, connected);
}
recipientPowerTransfer.connectedRecipients[recipient] = connected;
recipientPowerTransfer.connectionDirty[recipient] = false;
}
connectionDirty[c] = false;
}
}
@@ -296,7 +331,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 +356,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 +399,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;
@@ -302,17 +302,24 @@ namespace Barotrauma.Items.Components
/// <summary>
/// Returns the amount of power that can be supplied by batteries directly connected to the item
/// </summary>
protected float GetAvailableBatteryPower()
protected float GetAvailableInstantaneousBatteryPower()
{
var batteries = item.GetConnectedComponents<PowerContainer>();
if (item.Connections == null) { return 0.0f; }
float availablePower = 0.0f;
foreach (PowerContainer battery in batteries)
foreach (Connection c in item.Connections)
{
float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut);
availablePower += batteryPower;
}
var recipients = c.Recipients;
foreach (Connection recipient in recipients)
{
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
var battery = recipient.Item?.GetComponent<PowerContainer>();
if (battery == null) { continue; }
float maxOutputPerFrame = battery.MaxOutPut / 60.0f;
float framesPerMinute = 3600.0f;
availablePower += Math.Min(battery.Charge * framesPerMinute, maxOutputPerFrame);
}
}
return availablePower;
}
@@ -196,6 +196,15 @@ namespace Barotrauma.Items.Components
set;
}
private float deactivationTimer;
[Serialize(0f, false)]
public float DeactivationTime
{
get;
set;
}
public Body StickTarget
{
get;
@@ -207,6 +216,9 @@ namespace Barotrauma.Items.Components
get { return StickTarget != null; }
}
private Category originalCollisionCategories;
private Category originalCollisionTargets;
public Projectile(Item item, XElement element)
: base (item, element)
{
@@ -223,21 +235,26 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
if (Attack != null && Attack.DamageRange <= 0.0f && item.body != null)
if (item.body != null)
{
switch (item.body.BodyShape)
if (Attack != null && Attack.DamageRange <= 0.0f)
{
case PhysicsBody.Shape.Circle:
Attack.DamageRange = item.body.radius;
break;
case PhysicsBody.Shape.Capsule:
Attack.DamageRange = item.body.height / 2 + item.body.radius;
break;
case PhysicsBody.Shape.Rectangle:
Attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
break;
switch (item.body.BodyShape)
{
case PhysicsBody.Shape.Circle:
Attack.DamageRange = item.body.radius;
break;
case PhysicsBody.Shape.Capsule:
Attack.DamageRange = item.body.height / 2 + item.body.radius;
break;
case PhysicsBody.Shape.Rectangle:
Attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
break;
}
Attack.DamageRange = ConvertUnits.ToDisplayUnits(Attack.DamageRange);
}
Attack.DamageRange = ConvertUnits.ToDisplayUnits(Attack.DamageRange);
originalCollisionCategories = item.body.CollisionCategories;
originalCollisionTargets = item.body.CollidesWith;
}
}
@@ -259,6 +276,10 @@ namespace Barotrauma.Items.Components
launchPos = simPosition;
//set the rotation of the projectile again because dropping the projectile resets the rotation
Item.SetTransform(simPosition, rotation + (Item.body.Dir * LaunchRotationRadians));
if (DeactivationTime > 0)
{
deactivationTimer = DeactivationTime;
}
}
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f)
@@ -268,7 +289,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 +381,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;
@@ -542,6 +564,7 @@ namespace Barotrauma.Items.Components
{
if (fixture.Body.UserData is VoronoiCell) { return -1; }
if (fixture.Body.UserData is Entity entity && entity.Submarine != submarine) { return -1; }
if (fixture.Body.UserData is Limb limb && limb.character?.Submarine != submarine) { return -1; }
}
//ignore level cells if the item and the point of impact are inside a sub
@@ -583,7 +606,7 @@ namespace Barotrauma.Items.Components
{
if (dropper != null)
{
Deactivate();
DisableProjectileCollisions();
Unstick();
}
base.Drop(dropper);
@@ -591,6 +614,14 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (DeactivationTime > 0)
{
deactivationTimer -= deltaTime;
if (deactivationTimer < 0)
{
DisableProjectileCollisions();
}
}
while (impactQueue.Count > 0)
{
var impact = impactQueue.Dequeue();
@@ -612,8 +643,12 @@ namespace Barotrauma.Items.Components
}
//projectiles with a stickjoint don't become inactive until the stickjoint is detached
if (stickJoint == null && !item.body.FarseerBody.IsBullet)
{
IsActive = false;
{
IsActive = false;
if (DeactivationTime > 0 && deactivationTimer > 0)
{
DisableProjectileCollisions();
}
}
if (stickJoint == null) { return; }
@@ -713,7 +748,7 @@ namespace Barotrauma.Items.Components
}
if (hits.Count() >= MaxTargetsToHit || target.Body.UserData is VoronoiCell)
{
Deactivate();
DisableProjectileCollisions();
return true;
}
else
@@ -736,8 +771,9 @@ namespace Barotrauma.Items.Components
}
lastTarget = target;
float projectileNewSpeed = 0.5f;
float projectileDeflectedNewSpeed = 0.1f;
int remainingHits = Math.Max(MaxTargetsToHit - hits.Count, 0);
float speedMultiplier = Math.Min(0.4f + remainingHits * 0.1f, 1.0f);
float deflectedSpeedMultiplier = 0.1f;
AttackResult attackResult = new AttackResult();
Character character = null;
@@ -753,8 +789,8 @@ namespace Barotrauma.Items.Components
// when hitting limbs with piercing ammo, don't lose as much speed
if (MaxTargetsToHit > 1)
{
projectileNewSpeed = 1f;
projectileDeflectedNewSpeed = 0.8f;
speedMultiplier = 1f;
deflectedSpeedMultiplier = 0.8f;
}
if (limb.IsSevered || limb.character == null || limb.character.Removed) { return false; }
@@ -861,13 +897,13 @@ namespace Barotrauma.Items.Components
if (hits.Count() >= MaxTargetsToHit || hits.LastOrDefault()?.UserData is VoronoiCell)
{
Deactivate();
DisableProjectileCollisions();
}
if (attackResult.AppliedDamageModifiers != null &&
(attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles) && !StickToDeflective))
{
item.body.LinearVelocity *= projectileDeflectedNewSpeed;
item.body.LinearVelocity *= deflectedSpeedMultiplier;
}
else if ( // When hitting characters the collision normal seems to sometimes point into wrong direction, resulting in a failed attempt to stick
//Vector2.Dot(Vector2.Normalize(velocity), collisionNormal) < 0.0f &&
@@ -899,13 +935,13 @@ namespace Barotrauma.Items.Components
item.CreateServerEvent(this);
}
#endif
item.body.LinearVelocity *= projectileNewSpeed;
item.body.LinearVelocity *= speedMultiplier;
return Hitscan;
}
else
{
item.body.LinearVelocity *= projectileNewSpeed;
item.body.LinearVelocity *= speedMultiplier;
}
var containedItems = item.OwnInventory?.AllItems;
@@ -931,18 +967,26 @@ namespace Barotrauma.Items.Components
return true;
}
private void Deactivate()
private void DisableProjectileCollisions()
{
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
if ((item.Prefab.DamagedByProjectiles || item.Prefab.DamagedByMeleeWeapons) && item.Condition > 0)
if (originalCollisionCategories != Category.None && originalCollisionTargets != Category.None)
{
item.body.CollisionCategories = Physics.CollisionCharacter;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform | Physics.CollisionProjectile;
item.body.CollisionCategories = originalCollisionCategories;
item.body.CollidesWith = originalCollisionTargets;
}
else
{
item.body.CollisionCategories = Physics.CollisionItem;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
if ((item.Prefab.DamagedByProjectiles || item.Prefab.DamagedByMeleeWeapons) && item.Condition > 0)
{
item.body.CollisionCategories = Physics.CollisionCharacter;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform | Physics.CollisionProjectile;
}
else
{
item.body.CollisionCategories = Physics.CollisionItem;
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
}
}
IgnoredBodies.Clear();
}
@@ -993,7 +1037,14 @@ namespace Barotrauma.Items.Components
}
stickJoint = null;
}
if (!item.body.FarseerBody.IsBullet) { IsActive = false; }
if (!item.body.FarseerBody.IsBullet)
{
IsActive = false;
if (DeactivationTime > 0 && deactivationTimer > 0)
{
DisableProjectileCollisions();
}
}
item.GetComponent<Rope>()?.Snap();
if (stickTargetCharacter != null)
{
@@ -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,18 @@ 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))
{
IsActive = false;
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 +101,16 @@ 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;
IsActive = true;
break;
case "signal_in2":
if (signal.value == "0") return;
if (signal.value == "0") { return; }
timeSinceReceived[1] = 0.0f;
signalSender[1] = signal.sender;
IsActive = true;
break;
case "set_output":
output = signal.value;
@@ -19,11 +19,10 @@ 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)
{
IsActive = true;
RequiredSignalCount = element.GetChildElements("TerminalButton").Count(c => c.GetAttribute("style") != null);
if (RequiredSignalCount < 1)
{
@@ -88,25 +87,25 @@ namespace Barotrauma.Items.Components
}
}
var containers = item.GetComponents<ItemContainer>().ToList();
if (containers.Count != 1)
var containers = item.GetComponents<ItemContainer>();
if (containers.Count() != 1)
{
DebugConsole.ThrowError($"Error in item \"{item.Name}\": the ButtonTerminal component requires exactly one ItemContainer component!");
return;
}
Container = containers[0];
Container = containers.FirstOrDefault();
OnItemLoadedProjSpecific();
}
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")
{
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)]
@@ -236,6 +236,13 @@ namespace Barotrauma.Items.Components
{
ciElement.Connection = item.Connections?.FirstOrDefault(c => c.Name == ciElement.ConnectionName);
}
#if SERVER
//make sure the clients know about the states of the checkboxes and text fields
if (item.Submarine == null || !item.Submarine.Loading)
{
item.CreateServerEvent(this);
}
#endif
}
partial void UpdateLabelsProjSpecific();
@@ -301,7 +308,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 +324,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,22 +61,26 @@ namespace Barotrauma.Items.Components
public DelayComponent(Item item, XElement element)
: base (item, element)
{
signalQueue = new Queue<DelayedSignal>();
IsActive = true;
}
public override void Update(float deltaTime, Camera cam)
{
if (signalQueue.Count == 0)
{
IsActive = false;
return;
}
foreach (var val in signalQueue)
{
val.SendTimer -= 1;
}
while (signalQueue.Count > 0 && signalQueue.Peek().SendTimer <= 0)
{
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();
@@ -113,9 +119,10 @@ namespace Barotrauma.Items.Components
SendDuration = 1
};
signalQueue.Enqueue(prevQueuedSignal);
IsActive = true;
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;
}
}
}
}
@@ -26,6 +26,8 @@ namespace Barotrauma.Items.Components
public PhysicsBody ParentBody;
private bool isOn;
private Turret turret;
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
@@ -50,7 +52,7 @@ namespace Barotrauma.Items.Components
set
{
rotation = value;
SetLightSourceTransform();
SetLightSourceTransformProjSpecific();
}
}
@@ -85,12 +87,13 @@ namespace Barotrauma.Items.Components
[Editable, Serialize(false, true, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
public bool IsOn
{
get { return IsActive; }
get { return isOn; }
set
{
if (IsActive == value) { return; }
if (isOn == value && IsActive == value) { return; }
IsActive = value;
IsActive = isOn = value;
SetLightSourceState(value, value ? lightBrightness : 0.0f);
OnStateChanged();
}
}
@@ -170,7 +173,7 @@ namespace Barotrauma.Items.Components
#if CLIENT
if (Light != null)
{
Light.Color = IsActive ? lightColor : Color.Transparent;
Light.Color = IsOn ? lightColor : Color.Transparent;
}
#endif
}
@@ -200,9 +203,8 @@ namespace Barotrauma.Items.Components
set
{
if (base.IsActive == value) { return; }
base.IsActive = value;
SetLightSourceState(value, value ? lightBrightness : 0.0f);
base.IsActive = isOn = value;
SetLightSourceState(value, value ? lightBrightness : 0.0f);
}
}
@@ -237,6 +239,23 @@ namespace Barotrauma.Items.Components
turret = item.GetComponent<Turret>();
}
public override void OnMapLoaded()
{
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null && IsOn &&
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
SetLightSourceTransformProjSpecific();
base.IsActive = false;
isOn = true;
#if CLIENT
Light.ParentSub = item.Submarine;
#endif
}
}
public override void Update(float deltaTime, Camera cam)
{
if (item.AiTarget != null)
@@ -256,7 +275,7 @@ namespace Barotrauma.Items.Components
return;
}
SetLightSourceTransform();
SetLightSourceTransformProjSpecific();
PhysicsBody body = ParentBody ?? item.body;
if (body != null && !body.Enabled)
@@ -338,7 +357,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,18 @@ 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))
{
IsActive = false;
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");
@@ -8,12 +8,15 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class WifiComponent : ItemComponent
partial class WifiComponent : ItemComponent, IServerSerializable
{
private static readonly List<WifiComponent> list = new List<WifiComponent>();
const int ChannelMemorySize = 10;
private const int MinChannel = 0;
private const int MaxChannel = 10000;
private float range;
private int channel;
@@ -24,6 +27,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)]
@@ -48,7 +52,7 @@ namespace Barotrauma.Items.Components
get { return channel; }
set
{
channel = MathHelper.Clamp(value, 0, 10000);
channel = MathHelper.Clamp(value, MinChannel, MaxChannel);
}
}
@@ -113,6 +117,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))
{
@@ -227,6 +232,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);
}
@@ -301,7 +318,14 @@ namespace Barotrauma.Items.Components
case "set_channel":
if (int.TryParse(signal.value, out int newChannel))
{
int prevChannel = Channel;
Channel = newChannel;
if (prevChannel != Channel)
{
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
break;
case "set_range":
@@ -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,18 @@ 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))
{
IsActive = false;
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();
}
@@ -541,7 +542,7 @@ namespace Barotrauma.Items.Components
public bool HasPowerToShoot()
{
return GetAvailableBatteryPower() >= GetPowerRequiredToShoot();
return GetAvailableInstantaneousBatteryPower() >= GetPowerRequiredToShoot();
}
private bool TryLaunch(float deltaTime, Character character = null, bool ignorePower = false)
@@ -1441,6 +1442,11 @@ namespace Barotrauma.Items.Components
crosshairPointerSprite?.Remove(); crosshairPointerSprite = null;
moveSoundChannel?.Dispose(); moveSoundChannel = null;
WeaponIndicatorSprite?.Remove(); WeaponIndicatorSprite = null;
if (powerIndicator != null)
{
powerIndicator.RectTransform.Parent = null;
powerIndicator = null;
}
#endif
}
@@ -1516,7 +1522,7 @@ namespace Barotrauma.Items.Components
minRotation += MathHelper.TwoPi;
maxRotation += MathHelper.TwoPi;
}
rotation = (minRotation + maxRotation) / 2;
targetRotation = rotation = (minRotation + maxRotation) / 2;
UpdateTransformedBarrelPos();
}
@@ -1537,7 +1543,7 @@ namespace Barotrauma.Items.Components
minRotation += MathHelper.TwoPi;
maxRotation += MathHelper.TwoPi;
}
rotation = (minRotation + maxRotation) / 2;
targetRotation = rotation = (minRotation + maxRotation) / 2;
UpdateTransformedBarrelPos();
}
@@ -1607,6 +1613,7 @@ namespace Barotrauma.Items.Components
{
base.OnItemLoaded();
FindLightComponent();
targetRotation = rotation;
if (!loadedBaseRotation.HasValue)
{
if (item.FlippedX) { FlipX(relativeToSub: false); }
@@ -365,7 +365,16 @@ namespace Barotrauma.Items.Components
{
foreach (var allowedSlot in allowedSlots)
{
if (allowedSlot != InvSlotType.Any && !character.Inventory.IsInLimbSlot(item, allowedSlot)) { return; }
if (allowedSlot == InvSlotType.Any) { continue; }
foreach (Enum value in Enum.GetValues(typeof(InvSlotType)))
{
var slotType = (InvSlotType)value;
if (slotType == InvSlotType.Any || slotType == InvSlotType.None) { continue; }
if (allowedSlot.HasFlag(slotType) && !character.Inventory.IsInLimbSlot(item, slotType))
{
return;
}
}
}
picker = character;