Unstable 0.1500.1.0 (BaroDev edition)

This commit is contained in:
Markus Isberg
2021-09-03 21:56:31 +09:00
parent 501e02c026
commit e7b7c1a748
143 changed files with 2928 additions and 1356 deletions
@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
{
partial class GeneticMaterial : ItemComponent, IServerSerializable
{
private readonly string materialName;
private Character targetCharacter;
private AfflictionPrefab selectedEffect, selectedTaintedEffect;
[Serialize("", false)]
public string Effect
{
get;
set;
}
[Serialize("geneticmaterialdebuff", false)]
public string TaintedEffect
{
get;
set;
}
private bool tainted;
[Serialize(false, false)]
public bool Tainted
{
get { return tainted; }
private set
{
if (!value) { return; }
tainted = true;
item.AllowDeconstruct = false;
if (!string.IsNullOrEmpty(TaintedEffect))
{
selectedTaintedEffect = AfflictionPrefab.Prefabs.Where(a =>
a.Identifier.Equals(TaintedEffect, StringComparison.OrdinalIgnoreCase) ||
a.AfflictionType.Equals(TaintedEffect, StringComparison.OrdinalIgnoreCase)).GetRandom();
}
}
}
//only for saving the selected tainted effect
[Serialize("", false)]
public string SelectedTaintedEffect
{
get { return selectedTaintedEffect?.Identifier ?? string.Empty; }
private set
{
if (string.IsNullOrEmpty(value)) { return; }
selectedTaintedEffect = AfflictionPrefab.Prefabs.Find(a => a.Identifier == value);
}
}
public GeneticMaterial(Item item, XElement element)
: base(item, element)
{
string nameId = element.GetAttributeString("nameidentifier", "");
if (!string.IsNullOrEmpty(nameId))
{
materialName = TextManager.Get(nameId);
}
if (!string.IsNullOrEmpty(Effect))
{
selectedEffect = AfflictionPrefab.Prefabs.Where(a =>
a.Identifier.Equals(Effect, StringComparison.OrdinalIgnoreCase) ||
a.AfflictionType.Equals(Effect, StringComparison.OrdinalIgnoreCase)).GetRandom();
}
}
[Serialize(3.0f, false)]
public float ConditionIncreaseOnCombineMin { get; set; }
[Serialize(8.0f, false)]
public float ConditionIncreaseOnCombineMax { get; set; }
public bool CanBeCombinedWith(GeneticMaterial otherGeneticMaterial)
{
return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted;
}
public override void Equip(Character character)
{
if (character == null) { return; }
IsActive = true;
if (targetCharacter != null) { return; }
if (tainted)
{
if (selectedTaintedEffect != null)
{
float selectedTaintedEffectStrength = item.ConditionPercentage / 100.0f * selectedTaintedEffect.MaxStrength;
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(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));
targetCharacter = character;
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
if (targetCharacter != null)
{
if (!targetCharacter.HasEquippedItem(item) &&
(item.Container == null || !targetCharacter.HasEquippedItem(item.Container) || !(item.Container.GetComponent<ItemContainer>()?.AutoInject ?? false)))
{
item.ApplyStatusEffects(ActionType.OnSevered, 1.0f, targetCharacter);
var currentEffect = tainted ? selectedTaintedEffect : selectedEffect;
targetCharacter.CharacterHealth.ReduceAffliction(null, currentEffect.Identifier, currentEffect.MaxStrength);
targetCharacter = null;
IsActive = false;
}
}
}
public bool Combine(GeneticMaterial otherGeneticMaterial, Character user)
{
if (!CanBeCombinedWith(otherGeneticMaterial)) { return false; }
if (item.Prefab == otherGeneticMaterial.item.Prefab)
{
item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
float taintedProbability = GetTaintedProbabilityOnRefine(user);
if (taintedProbability >= Rand.Range(0.0f, 1.0f))
{
MakeTainted();
}
return true;
}
else
{
item.Condition = otherGeneticMaterial.Item.Condition =
(item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
item.OwnInventory?.TryPutItem(otherGeneticMaterial.Item, user: null);
MakeTainted();
return false;
}
}
private float GetTaintedProbabilityOnRefine(Character user)
{
if (user == null) { return 1.0f; }
float probability = MathHelper.Lerp(0.0f, 0.99f, item.Condition / 100.0f);
probability *= MathHelper.Lerp(1.0f, 0.25f, DegreeOfSuccess(user));
return probability;
}
private void MakeTainted()
{
if (GameMain.NetworkMember?.IsClient ?? false) { return; }
Tainted = true;
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
}
@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
if (aim)
{
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, false, hitPos, holdAngle + hitPos);
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, false, hitPos, holdAngle + hitPos, aimingMelee: true);
}
else
{
@@ -356,6 +356,7 @@ namespace Barotrauma.Items.Components
if (Attack != null)
{
Attack.SetUser(User);
Attack.DamageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
if (targetLimb != null)
{
@@ -107,7 +107,7 @@ namespace Barotrauma.Items.Components
if (ReloadTimer < 0.0f)
{
ReloadTimer = 0.0f;
// was this an optimization or related to something else? currently disabled for charge-type weapons
// was this an optimization or related to something else? it cannot occur for charge-type weapons
//IsActive = false;
if (MaxChargeTime == 0.0f)
{
@@ -118,7 +118,7 @@ namespace Barotrauma.Items.Components
float previousChargeTime = currentChargeTime;
float chargeDeltaTime = tryingToCharge ? deltaTime : -deltaTime;
float chargeDeltaTime = tryingToCharge && ReloadTimer <= 0f ? deltaTime : -deltaTime;
currentChargeTime = Math.Clamp(currentChargeTime + chargeDeltaTime, 0f, MaxChargeTime);
tryingToCharge = false;
@@ -982,7 +982,7 @@ namespace Barotrauma.Items.Components
AIObjectiveContainItem containObjective = null;
if (character.AIController is HumanAIController aiController)
{
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
containObjective = new AIObjectiveContainItem(character, container.ContainableItemIdentifiers.ToArray(), container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
{
targetItemCount = itemCount,
Equip = equip,
@@ -5,6 +5,7 @@ using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using FarseerPhysics;
using System.Collections.Immutable;
namespace Barotrauma.Items.Components
{
@@ -23,6 +24,28 @@ namespace Barotrauma.Items.Components
}
}
class SlotRestrictions
{
public readonly int MaxStackSize;
public readonly List<RelatedItem> ContainableItems;
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems)
{
MaxStackSize = maxStackSize;
ContainableItems = containableItems;
}
public bool MatchesItem(Item item)
{
return ContainableItems == null || ContainableItems.Count == 0 || ContainableItems.Any(c => c.MatchesItem(item));
}
public bool MatchesItem(ItemPrefab itemPrefab)
{
return ContainableItems == null || ContainableItems.Count == 0 || ContainableItems.Any(c => c.MatchesItem(itemPrefab));
}
}
private bool alwaysContainedItemsSpawned;
public ItemInventory Inventory;
@@ -73,6 +96,7 @@ namespace Barotrauma.Items.Components
#endif
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
public Vector2 ItemInterval { get; set; }
[Serialize(100, false, description: "How many items are placed in a row before starting a new row.")]
public int ItemsPerRow { get; set; }
@@ -90,7 +114,6 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false, description: "If set to true, interacting with this item will make the character interact with the contained item(s), automatically picking them up if they can be picked up.")]
public bool AutoInteractWithContained
{
@@ -98,6 +121,9 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, false)]
public bool AllowAccess { get; set; }
[Serialize(false, false)]
public bool AccessOnlyWhenBroken { get; set; }
@@ -147,7 +173,7 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(0.5f, false, description: "The rotation in which the contained sprites are drawn (in degrees).")]
[Serialize(0.5f, false, description: "The health threshold that the user must reach in order to activate the autoinjection.")]
public float AutoInjectThreshold
{
get;
@@ -157,10 +183,12 @@ namespace Barotrauma.Items.Components
[Serialize(false, false)]
public bool RemoveContainedItemsOnDeconstruct { get; set; }
private SlotRestrictions[] slotRestrictions;
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
{
isRestrictionsDefined = containableRestrictions.Any();
if (ContainableItems.None(ri => ri.MatchesItem(item))) { return false; }
if (slotRestrictions.None(s => s.MatchesItem(item))) { return false; }
if (!isRestrictionsDefined) { return true; }
return identifiersOrTags.Any(id => containableRestrictions.Any(r => r == id));
}
@@ -168,22 +196,22 @@ namespace Barotrauma.Items.Components
public bool ShouldBeContained(Item item, out bool isRestrictionsDefined)
{
isRestrictionsDefined = containableRestrictions.Any();
if (ContainableItems.None(ri => ri.MatchesItem(item))) { return false; }
if (slotRestrictions.None(s => s.MatchesItem(item))) { return false; }
if (!isRestrictionsDefined) { return true; }
return containableRestrictions.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
}
public List<RelatedItem> ContainableItems { get; private set; } = new List<RelatedItem>();
public IEnumerable<string> GetContainableItemIdentifiers => ContainableItems.SelectMany(ri => ri.Identifiers);
private ImmutableHashSet<string> containableItemIdentifiers;
public IEnumerable<string> ContainableItemIdentifiers => containableItemIdentifiers;
public override bool RecreateGUIOnResolutionChange => true;
public ItemContainer(Item item, XElement element)
: base (item, element)
: base(item, element)
{
Inventory = new ItemInventory(item, this, capacity, SlotsPerRow);
int totalCapacity = capacity;
List<RelatedItem> containableItems = null;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -195,34 +223,92 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
continue;
}
ContainableItems.Add(containable);
containableItems ??= new List<RelatedItem>();
containableItems.Add(containable);
break;
case "subcontainer":
totalCapacity += subElement.GetAttributeInt("capacity", 1);
break;
}
}
Inventory = new ItemInventory(item, this, totalCapacity, SlotsPerRow);
slotRestrictions = new SlotRestrictions[totalCapacity];
for (int i = 0; i < capacity; i++)
{
slotRestrictions[i] = new SlotRestrictions(maxStackSize, containableItems);
}
int subContainerIndex = capacity;
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "subcontainer") { continue; }
int subCapacity = subElement.GetAttributeInt("capacity", 1);
int subMaxStackSize = subElement.GetAttributeInt("maxstacksize", maxStackSize);
List<RelatedItem> subContainableItems = null;
foreach (XElement subSubElement in subElement.Elements())
{
if (subSubElement.Name.ToString().ToLowerInvariant() != "containable") { continue; }
RelatedItem containable = RelatedItem.Load(subSubElement, returnEmpty: false, parentDebugName: item.Name);
if (containable == null)
{
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
continue;
}
subContainableItems ??= new List<RelatedItem>();
subContainableItems.Add(containable);
}
for (int i = subContainerIndex; i < subContainerIndex + subCapacity; i++)
{
slotRestrictions[i] = new SlotRestrictions(subMaxStackSize, subContainableItems);
}
subContainerIndex += subCapacity;
}
capacity = totalCapacity;
InitProjSpecific(element);
}
public int GetMaxStackSize(int slotIndex)
{
if (slotIndex < 0 || slotIndex >= capacity)
{
return 0;
}
return slotRestrictions[slotIndex].MaxStackSize;
}
partial void InitProjSpecific(XElement element);
public void OnItemContained(Item containedItem)
{
item.SetContainedItemPositions();
RelatedItem ri = ContainableItems.Find(x => x.MatchesItem(containedItem));
if (ri != null)
int index = Inventory.FindIndex(containedItem);
if (index >= 0 && index < slotRestrictions.Length)
{
activeContainedItems.RemoveAll(i => i.Item == containedItem);
foreach (StatusEffect effect in ri.statusEffects)
RelatedItem ri = slotRestrictions[index].ContainableItems?.Find(ci => ci.MatchesItem(containedItem));
if (ri != null)
{
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, ri.ExcludeBroken));
activeContainedItems.RemoveAll(i => i.Item == containedItem);
foreach (StatusEffect effect in ri.statusEffects)
{
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, ri.ExcludeBroken));
}
}
}
}
//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);
}
public override void Move(Vector2 amount)
{
SetContainedItemPositions();
}
public void OnItemRemoved(Item containedItem)
{
activeContainedItems.RemoveAll(i => i.Item == containedItem);
@@ -233,13 +319,11 @@ namespace Barotrauma.Items.Components
public bool CanBeContained(Item item)
{
if (ContainableItems.Count == 0) { return true; }
return ContainableItems.Find(c => c.MatchesItem(item)) != null;
return slotRestrictions.Any(s => s.MatchesItem(item));
}
public bool CanBeContained(ItemPrefab itemPrefab)
{
if (ContainableItems.Count == 0) { return true; }
return ContainableItems.Find(c => c.MatchesItem(itemPrefab)) != null;
return slotRestrictions.Any(s => s.MatchesItem(itemPrefab));
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
@@ -264,6 +348,7 @@ namespace Barotrauma.Items.Components
foreach (Item item in Inventory.AllItemsMod)
{
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter);
item.GetComponent<GeneticMaterial>()?.Equip(ownerCharacter);
}
}
}
@@ -304,11 +389,12 @@ namespace Barotrauma.Items.Components
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
{
return (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
return AllowAccess && (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
}
public override bool Select(Character character)
{
if (!AllowAccess) { return false; }
if (item.Container != null) { return false; }
if (AccessOnlyWhenBroken)
{
@@ -335,6 +421,7 @@ namespace Barotrauma.Items.Components
public override bool Pick(Character picker)
{
if (!AllowAccess) { return false; }
if (AccessOnlyWhenBroken)
{
if (item.Condition > 0)
@@ -362,7 +449,7 @@ namespace Barotrauma.Items.Components
public override bool Combine(Item item, Character user)
{
if (!AllowDragAndDrop && user != null) { return false; }
if (!ContainableItems.Any(it => it.MatchesItem(item))) { return false; }
if (!slotRestrictions.Any(s => s.MatchesItem(item))) { return false; }
if (user != null && !user.CanAccessInventory(Inventory)) { return false; }
if (Inventory.TryPutItem(item, user))
@@ -392,50 +479,59 @@ namespace Barotrauma.Items.Components
Vector2 transformedItemInterval = ItemInterval * item.Scale;
Vector2 transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
Vector2 transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y);
if (item.body == null)
if (ItemPos == Vector2.Zero && ItemInterval == Vector2.Zero)
{
if (item.FlippedX)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemPos.X += item.Rect.Width;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
if (item.FlippedY)
{
transformedItemPos.Y = -transformedItemPos.Y;
transformedItemPos.Y -= item.Rect.Height;
transformedItemInterval.Y = -transformedItemInterval.Y;
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
}
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
if (Math.Abs(item.Rotation) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
}
transformedItemPos = item.Position;
}
else
{
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
if (item.body.Dir == -1.0f)
if (item.body == null)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
if (item.FlippedX)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemPos.X += item.Rect.Width;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
if (item.FlippedY)
{
transformedItemPos.Y = -transformedItemPos.Y;
transformedItemPos.Y -= item.Rect.Height;
transformedItemInterval.Y = -transformedItemInterval.Y;
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
}
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
if (Math.Abs(item.Rotation) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
}
}
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.Position;
}
else
{
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
if (item.body.Dir == -1.0f)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.Position;
}
}
float currentRotation = itemRotation;
if (item.body != null)
{
currentRotation *= item.body.Dir;
currentRotation += item.body.Rotation;
}
@@ -492,6 +588,7 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
containableItemIdentifiers = slotRestrictions.SelectMany(s => s.ContainableItems?.SelectMany(ri => ri.Identifiers) ?? Enumerable.Empty<string>()).ToImmutableHashSet();
if (item.Submarine == null || !item.Submarine.Loading)
{
SpawnAlwaysContainedItems();
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Abilities;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
@@ -14,6 +15,10 @@ namespace Barotrauma.Items.Components
private bool hasPower;
private Character user;
private float userDeconstructorSpeedMultiplier = 1.0f;
private ItemContainer inputContainer, outputContainer;
public ItemContainer InputContainer
@@ -25,7 +30,10 @@ namespace Barotrauma.Items.Components
{
get { return outputContainer; }
}
[Serialize(false, true)]
public bool DeconstructItemsSimultaneously { get; set; }
[Editable, Serialize(1.0f, true)]
public float DeconstructionSpeed { get; set; }
@@ -81,65 +89,149 @@ namespace Barotrauma.Items.Components
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
progressTimer += deltaTime * Math.Min(Voltage, 1.0f);
var targetItem = inputContainer.Inventory.LastOrDefault();
if (targetItem == null) { return; }
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / DeconstructionSpeed : 1.0f;
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
if (DeconstructItemsSimultaneously)
{
// In multiplayer, the server handles the deconstruction into new items
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (targetItem.Prefab.RandomDeconstructionOutput)
float deconstructTime = 0.0f;
foreach (Item targetItem in inputContainer.Inventory.AllItems)
{
int amount = targetItem.Prefab.RandomDeconstructionOutputAmount;
List<int> deconstructItemIndexes = new List<int>();
for (int i = 0; i < targetItem.Prefab.DeconstructItems.Count; i++)
{
deconstructItemIndexes.Add(i);
}
List<float> commonness = targetItem.Prefab.DeconstructItems.Select(i => i.Commonness).ToList();
List<DeconstructItem> products = new List<DeconstructItem>();
for (int i = 0; i < amount; i++)
{
if (deconstructItemIndexes.Count < 1) { break; }
var itemIndex = ToolBox.SelectWeightedRandom(deconstructItemIndexes, commonness, Rand.RandSync.Unsynced);
products.Add(targetItem.Prefab.DeconstructItems[itemIndex]);
var removeIndex = deconstructItemIndexes.IndexOf(itemIndex);
deconstructItemIndexes.RemoveAt(removeIndex);
commonness.RemoveAt(removeIndex);
}
foreach (DeconstructItem deconstructProduct in products)
{
CreateDeconstructProduct(deconstructProduct);
}
}
else
{
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
{
CreateDeconstructProduct(deconstructProduct);
}
deconstructTime += targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * userDeconstructorSpeedMultiplier);
}
void CreateDeconstructProduct(DeconstructItem deconstructProduct)
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
{
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab))
List<Item> items = inputContainer.Inventory.AllItems.ToList();
foreach (Item targetItem in items)
{
DebugConsole.ThrowError("Tried to deconstruct item \"" + targetItem.Name + "\" but couldn't find item prefab \"" + deconstructProduct.ItemIdentifier + "\"!");
return;
if ((Entity.Spawner?.IsInRemoveQueue(targetItem) ?? false) || !inputContainer.Inventory.AllItems.Contains(targetItem)) { continue; }
var validDeconstructItems = targetItem.Prefab.DeconstructItems.FindAll(it =>
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) &&
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)))));
ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
}
#if SERVER
item.CreateServerEvent(this);
#endif
progressTimer = 0.0f;
progressState = 0.0f;
float condition = deconstructProduct.CopyCondition ?
percentageHealth * itemPrefab.Health :
itemPrefab.Health * deconstructProduct.OutCondition;
}
}
else
{
var targetItem = inputContainer.Inventory.LastOrDefault();
if (targetItem == null) { return; }
var validDeconstructItems = targetItem.Prefab.DeconstructItems.FindAll(it =>
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)));
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / DeconstructionSpeed : 1.0f;
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
{
ProcessItem(targetItem, inputContainer.Inventory.AllItemsMod, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
#if SERVER
item.CreateServerEvent(this);
#endif
progressTimer = 0.0f;
progressState = 0.0f;
}
}
}
private void ProcessItem(Item targetItem, IEnumerable<Item> inputItems, List<DeconstructItem> validDeconstructItems, bool allowRemove = true)
{
// In multiplayer, the server handles the deconstruction into new items
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (targetItem.Prefab.RandomDeconstructionOutput)
{
int amount = targetItem.Prefab.RandomDeconstructionOutputAmount;
List<int> deconstructItemIndexes = new List<int>();
for (int i = 0; i < validDeconstructItems.Count; i++)
{
deconstructItemIndexes.Add(i);
}
List<float> commonness = validDeconstructItems.Select(i => i.Commonness).ToList();
List<DeconstructItem> products = new List<DeconstructItem>();
for (int i = 0; i < amount; i++)
{
if (deconstructItemIndexes.Count < 1) { break; }
var itemIndex = ToolBox.SelectWeightedRandom(deconstructItemIndexes, commonness, Rand.RandSync.Unsynced);
products.Add(validDeconstructItems[itemIndex]);
var removeIndex = deconstructItemIndexes.IndexOf(itemIndex);
deconstructItemIndexes.RemoveAt(removeIndex);
commonness.RemoveAt(removeIndex);
}
user.CheckTalents(AbilityEffectType.OnItemDeconstructed, targetItem);
foreach (DeconstructItem deconstructProduct in products)
{
CreateDeconstructProduct(deconstructProduct, inputItems);
}
}
else
{
foreach (DeconstructItem deconstructProduct in validDeconstructItems)
{
CreateDeconstructProduct(deconstructProduct, inputItems);
}
}
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems)
{
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab))
{
DebugConsole.ThrowError("Tried to deconstruct item \"" + targetItem.Name + "\" but couldn't find item prefab \"" + deconstructProduct.ItemIdentifier + "\"!");
return;
}
float condition = deconstructProduct.CopyCondition ?
percentageHealth * itemPrefab.Health :
itemPrefab.Health * Rand.Range(deconstructProduct.OutConditionMin, deconstructProduct.OutConditionMax);
if (DeconstructItemsSimultaneously && deconstructProduct.RequiredOtherItem.Length > 0)
{
foreach (Item otherItem in inputItems)
{
if (targetItem == otherItem) { continue; }
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r.Equals(otherItem.Prefab.Identifier, StringComparison.OrdinalIgnoreCase)))
{
var geneticMaterial1 = targetItem.GetComponent<GeneticMaterial>();
var geneticMaterial2 = otherItem.GetComponent<GeneticMaterial>();
if (geneticMaterial1 != null && geneticMaterial2 != null)
{
if (geneticMaterial1.Combine(geneticMaterial2, user))
{
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddToRemoveQueue(otherItem);
}
allowRemove = false;
return;
}
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddToRemoveQueue(otherItem);
}
}
}
var itemsCreated = new AbilityValue(1f);
user.CheckTalents(AbilityEffectType.OnItemDeconstructedMaterial, (targetItem.Prefab, itemsCreated));
int amount = (int)itemsCreated.Value;
for (int i = 0; i < amount; i++)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
{
for (int i = 0; i < outputContainer.Capacity; i++)
@@ -153,36 +245,31 @@ namespace Barotrauma.Items.Components
PutItemsToLinkedContainer();
});
}
}
if (targetItem.Prefab.AllowDeconstruct)
if (targetItem.AllowDeconstruct && allowRemove)
{
//drop all items that are inside the deconstructed item
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
{
//drop all items that are inside the deconstructed item
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
{
if (ic?.Inventory == null || ic.RemoveContainedItemsOnDeconstruct) { continue; }
ic.Inventory.AllItemsMod.ForEach(containedItem => outputContainer.Inventory.TryPutItem(containedItem, user: null));
}
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
if (ic?.Inventory == null || ic.RemoveContainedItemsOnDeconstruct) { continue; }
ic.Inventory.AllItemsMod.ForEach(containedItem => outputContainer.Inventory.TryPutItem(containedItem, user: null));
}
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
else
{
if (!outputContainer.Inventory.CanBePut(targetItem) || (Entity.Spawner?.IsInRemoveQueue(targetItem) ?? false))
{
targetItem.Drop(dropper: null);
}
else
{
if (!outputContainer.Inventory.CanBePut(targetItem))
{
targetItem.Drop(dropper: null);
}
else
{
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
}
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
}
#if SERVER
item.CreateServerEvent(this);
#endif
progressTimer = 0.0f;
progressState = 0.0f;
}
}
@@ -190,7 +277,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (outputContainer.Inventory.IsEmpty()) { return; }
foreach (MapEntity linkedTo in item.linkedTo)
{
if (linkedTo is Item linkedItem)
@@ -201,7 +288,7 @@ namespace Barotrauma.Items.Components
if (itemContainer == null) { continue; }
outputContainer.Inventory.AllItemsMod.ForEach(containedItem => itemContainer.Inventory.TryPutItem(containedItem, user: null, createNetworkEvent: true));
}
}
}
}
/// <summary>
@@ -221,14 +308,54 @@ namespace Barotrauma.Items.Components
}
}
private IEnumerable<(Item item, DeconstructItem output)> GetAvailableOutputs(bool checkRequiredOtherItems = true)
{
var items = inputContainer.Inventory.AllItems;
foreach (Item inputItem in items)
{
if (!inputItem.AllowDeconstruct) { continue; }
foreach (var deconstructItem in inputItem.Prefab.DeconstructItems)
{
if (deconstructItem.RequiredDeconstructor.Length > 0)
{
if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) { continue; }
}
if (deconstructItem.RequiredOtherItem.Length > 0 && checkRequiredOtherItems)
{
if (!deconstructItem.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)))) { continue; }
bool validOtherItemFound = false;
foreach (Item otherInputItem in items)
{
if (otherInputItem == inputItem) { continue; }
if (!deconstructItem.RequiredOtherItem.Any(r => otherInputItem.HasTag(r) || otherInputItem.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) { continue; }
var geneticMaterial1 = inputItem.GetComponent<GeneticMaterial>();
var geneticMaterial2 = otherInputItem.GetComponent<GeneticMaterial>();
if (geneticMaterial1 != null && geneticMaterial2 != null)
{
if (!geneticMaterial1.CanBeCombinedWith(geneticMaterial2)) { continue; }
}
validOtherItemFound = true;
}
if (!validOtherItemFound) { continue; }
}
yield return (inputItem, deconstructItem);
}
}
}
private void SetActive(bool active, Character user = null)
{
PutItemsToLinkedContainer();
this.user = user;
if (inputContainer.Inventory.IsEmpty()) { active = false; }
IsActive = active;
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
userDeconstructorSpeedMultiplier = user != null ? 1f + user.GetStatValue(StatTypes.DeconstructorSpeedMultiplier) : 1f;
#if SERVER
if (user != null)
{
@@ -241,10 +368,6 @@ namespace Barotrauma.Items.Components
progressState = 0.0f;
}
#if CLIENT
activateButton.Text = TextManager.Get(IsActive ? "DeconstructorCancel" : "DeconstructorDeconstruct");
#endif
inputContainer.Inventory.Locked = IsActive;
}
}
@@ -316,7 +316,7 @@ namespace Barotrauma.Items.Components
availablePrefab.Condition -= availablePrefab.Prefab.Health * requiredItem.MinCondition;
continue;
}
availablePrefabs.Remove(availablePrefab);
Entity.Spawner.AddToRemoveQueue(availablePrefab);
inputContainer.Inventory.RemoveItem(availablePrefab);
@@ -324,18 +324,20 @@ namespace Barotrauma.Items.Components
}
});
Character tempUser = user;
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition * fabricatedItem.TargetItem.Health);
var itemsCreated = new AbilityValue(fabricatedItem.Amount);
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
var fabricationValueItem = new AbilityValueItem(fabricatedItem.Amount, fabricatedItem.TargetItem);
if (user != null)
{
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, (fabricatedItem.TargetItem, itemsCreated));
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
{
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationValueItem);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
}
tempUser.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, (fabricatedItem.TargetItem, itemsCreated));
for (int i = 0; i < (int)itemsCreated.Value; i++)
var tempUser = user;
for (int i = 0; i < (int)fabricationValueItem.Value; i++)
{
if (i < amountFittingContainer)
{
@@ -359,14 +361,13 @@ namespace Barotrauma.Items.Components
}
}
}
if (user?.Info != null && !user.Removed)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
{
float userSkill = user.GetSkillLevel(skill.Identifier);
float addedSkill = skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f);
var addedSkillValue = new AbilityValue(0f);
var addedSkillValue = new AbilityValueString(0f, skill.Identifier);
user.CheckTalents(AbilityEffectType.OnItemFabricationSkillGain, addedSkillValue);
user.Info.IncreaseSkillLevel(
@@ -365,16 +365,13 @@ namespace Barotrauma.Items.Components
item.SendSignal(new Signal(velY.ToString(CultureInfo.InvariantCulture), sender: user), "velocity_y_out");
// converts the controlled sub's velocity to km/h and sends it.
// TODO: add current_velocity_x and current_velocity_y pins on the navigation terminals and shuttle terminals
// TODO: increase the size of the connection panels of both navigation terminals
if (controlledSub is { } sub)
{
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.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_depth");
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_y");
}
// if our tactical AI pilot has left, revert back to maintaining position
@@ -15,6 +15,9 @@ namespace Barotrauma.Items.Components
//a list of connections a given connection is connected to, either directly or via other power transfer components
private readonly Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
private float overloadCooldownTimer;
private const float OverloadCooldown = 5.0f;
protected float powerLoad;
protected bool isBroken;
@@ -173,12 +176,19 @@ namespace Barotrauma.Items.Components
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
if (overloadCooldownTimer > 0.0f)
{
overloadCooldownTimer -= deltaTime;
return;
}
//damage the item if voltage is too high (except if running as a client)
float prevCondition = item.Condition;
item.Condition -= deltaTime * 10.0f;
if (item.Condition <= 0.0f && prevCondition > 0.0f)
{
overloadCooldownTimer = OverloadCooldown;
#if CLIENT
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
Vector2 baseVel = Rand.Vector(300.0f);
@@ -0,0 +1,96 @@
using Microsoft.Xna.Framework;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class RemoteController : ItemComponent
{
[Serialize("", false, description: "Tag or identifier of the item that should be controlled.")]
public string Target
{
get;
private set;
}
[Serialize(false, false)]
public bool OnlyInOwnSub
{
get;
private set;
}
[Serialize(10000.0f, false)]
public float Range
{
get;
private set;
}
public Item TargetItem { get => currentTarget; }
private Item currentTarget;
private Character currentUser;
private Submarine currentSub;
public RemoteController(Item item, XElement element)
: base(item, element)
{
}
public override bool Select(Character character)
{
if (base.Select(character))
{
FindTarget(character);
return true;
}
return false;
}
public override void Equip(Character character)
{
FindTarget(character);
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
if (currentTarget.Removed ||
item.Submarine != currentSub ||
Vector2.DistanceSquared(currentTarget.WorldPosition, item.WorldPosition) > Range * Range)
{
FindTarget(currentUser);
}
}
private void FindTarget(Character user)
{
currentTarget = null;
if (user == null || (item.Submarine == null && OnlyInOwnSub))
{
IsActive = false;
return;
}
float closestDist = float.PositiveInfinity;
foreach (Item targetItem in Item.ItemList)
{
if (OnlyInOwnSub)
{
if (targetItem.Submarine != item.Submarine) { continue; }
if (targetItem.Submarine.TeamID != user.TeamID) { continue; }
}
if (!targetItem.HasTag(Target) && targetItem.prefab.Identifier != Target) { continue; }
float distSqr = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
if (distSqr > Range * Range || distSqr > closestDist) { continue; }
currentTarget = targetItem;
currentSub = item.Submarine;
closestDist = distSqr;
currentUser = user;
}
IsActive = currentTarget != null;
}
}
}
@@ -339,7 +339,7 @@ namespace Barotrauma.Items.Components
if (currentFixerAction == FixActions.Tinker)
{
// this is a bit code rotty to interject it here, should be less reliant on returning
// not great to interject it here, should be less reliant on returning
if (!CanTinker(CurrentFixer))
{
StopRepairing(CurrentFixer);
@@ -134,20 +134,30 @@ namespace Barotrauma.Items.Components
foreach (Wire wire in c.Wires)
{
if (wire == null) { continue; }
#if CLIENT
if (wire.Item.IsSelected) { continue; }
#endif
var wireNodes = wire.GetNodes();
if (wireNodes.Count == 0) { continue; }
TryMoveWire(wire);
}
}
if (Submarine.RectContains(item.Rect, wireNodes[0] + wireNodeOffset))
{
wire.MoveNode(0, amount);
}
else if (Submarine.RectContains(item.Rect, wireNodes[wireNodes.Count - 1] + wireNodeOffset))
{
wire.MoveNode(wireNodes.Count - 1, amount);
}
foreach (var wire in DisconnectedWires)
{
TryMoveWire(wire);
}
void TryMoveWire(Wire wire)
{
#if CLIENT
if (wire.Item.IsSelected) { return; }
#endif
var wireNodes = wire.GetNodes();
if (wireNodes.Count == 0) { return; }
if (Submarine.RectContains(item.Rect, wireNodes[0] + wireNodeOffset))
{
wire.MoveNode(0, amount);
}
else if (Submarine.RectContains(item.Rect, wireNodes[wireNodes.Count - 1] + wireNodeOffset))
{
wire.MoveNode(wireNodes.Count - 1, amount);
}
}
}
@@ -41,7 +41,7 @@ namespace Barotrauma.Items.Components
set
{
if (string.IsNullOrEmpty(value)) { return; }
ShowOnDisplay(value);
ShowOnDisplay(value, addToHistory: true);
}
}
@@ -59,7 +59,7 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element);
partial void ShowOnDisplay(string input, bool addToHistory = true);
partial void ShowOnDisplay(string input, bool addToHistory);
public override void ReceiveSignal(Signal signal, Connection connection)
{
@@ -70,14 +70,14 @@ namespace Barotrauma.Items.Components
}
string inputSignal = signal.value.Replace("\\n", "\n");
ShowOnDisplay(inputSignal);
ShowOnDisplay(inputSignal, addToHistory: true);
}
public override void OnItemLoaded()
{
bool isSubEditor = false;
#if CLIENT
isSubEditor = Screen.Selected != GameMain.SubEditorScreen || GameMain.GameSession?.GameMode is TestGameMode;
isSubEditor = Screen.Selected == GameMain.SubEditorScreen || GameMain.GameSession?.GameMode is TestGameMode;
#endif
base.OnItemLoaded();
@@ -110,7 +110,7 @@ namespace Barotrauma.Items.Components
{
string msg = componentElement.GetAttributeString("msg" + i, null);
if (msg == null) { break; }
ShowOnDisplay(msg);
ShowOnDisplay(msg, addToHistory: true);
}
}
}
@@ -133,15 +133,15 @@ namespace Barotrauma.Items.Components
public bool IsConnectedTo(Item item)
{
if (connections[0] != null && connections[0].Item == item) return true;
return (connections[1] != null && connections[1].Item == item);
if (connections[0] != null && connections[0].Item == item) { return true; }
return connections[1] != null && connections[1].Item == item;
}
public void RemoveConnection(Item item)
{
for (int i = 0; i < 2; i++)
{
if (connections[i] == null || connections[i].Item != item) continue;
if (connections[i] == null || connections[i].Item != item) { continue; }
foreach (Wire wire in connections[i].Wires)
{
@@ -64,6 +64,8 @@ namespace Barotrauma.Items.Components
private Character currentTarget;
const float aiFindTargetInterval = 5.0f;
private const float TinkeringPowerCostReduction = 1.25f;
public float Rotation
{
get { return rotation; }
@@ -504,9 +506,19 @@ namespace Barotrauma.Items.Components
return TryLaunch(deltaTime, character);
}
public float GetPowerRequiredToShoot()
{
float powerCost = powerConsumption;
if (user != null)
{
powerCost /= (1 + user.GetStatValue(StatTypes.TurretPowerCostReduction));
}
return powerCost;
}
public bool HasPowerToShoot()
{
return GetAvailableBatteryPower() >= powerConsumption;
return GetAvailableBatteryPower() >= GetPowerRequiredToShoot();
}
private bool TryLaunch(float deltaTime, Character character = null, bool ignorePower = false)
@@ -617,10 +629,12 @@ namespace Barotrauma.Items.Components
if (!ignorePower)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
float neededPower = powerConsumption;
float neededPower = GetPowerRequiredToShoot();
// tinkering is currently not factored into the common method as it is checked only when shooting
// but this is a minor issue that causes mostly cosmetic woes. might still be worth refactoring later
if (isTinkering)
{
neededPower /= 1.25f;
neededPower /= TinkeringPowerCostReduction;
}
while (neededPower > 0.0001f && batteries.Count > 0)
{
@@ -1022,7 +1036,7 @@ namespace Barotrauma.Items.Components
container = containerItem.GetComponent<ItemContainer>();
if (container != null) { break; }
}
if (container == null || container.ContainableItems.Count == 0)
if (container == null || !container.ContainableItemIdentifiers.Any())
{
if (character.IsOnPlayerTeam)
{
@@ -1046,7 +1060,7 @@ namespace Barotrauma.Items.Components
{
if (!character.IsOnPlayerTeam) { return; }
if (character.Submarine != Submarine.MainSub) { return; }
string ammoType = container.ContainableItems.First().Identifiers.FirstOrDefault() ?? "ammobox";
string ammoType = container.ContainableItemIdentifiers.FirstOrDefault() ?? "ammobox";
int remainingAmmo = Submarine.MainSub.GetItems(false).Count(i => i.HasTag(ammoType) && i.Condition > 1);
if (remainingAmmo == 0)
{
@@ -55,6 +55,8 @@ namespace Barotrauma
public float Scale { get; private set; }
public float Rotation { get; private set; }
public LimbType DepthLimb { get; private set; }
private Wearable _wearableComponent;
public Wearable WearableComponent
@@ -177,6 +179,7 @@ namespace Barotrauma
DepthLimb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("depthlimb", "None"), true);
Sound = SourceElement.GetAttributeString("sound", "");
Scale = SourceElement.GetAttributeFloat("scale", 1.0f);
Rotation = MathHelper.ToRadians(SourceElement.GetAttributeFloat("rotation", 0.0f));
var index = SourceElement.GetAttributePoint("sheetindex", new Point(-1, -1));
if (index.X > -1 && index.Y > -1)
{