Unstable 0.1500.1.0 (BaroDev edition)
This commit is contained in:
@@ -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();
|
||||
|
||||
+205
-82
@@ -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);
|
||||
|
||||
+23
-13
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user