Merge branch 'master' of https://github.com/Regalis11/Barotrauma.git
This commit is contained in:
@@ -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;
|
||||
|
||||
+20
-14
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
+28
-4
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+15
-1
@@ -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)
|
||||
{
|
||||
|
||||
+13
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-10
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user