Release v0.15.12.0
This commit is contained in:
+232
-79
@@ -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,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool hasPower;
|
||||
|
||||
private Character user;
|
||||
|
||||
private float userDeconstructorSpeedMultiplier = 1.0f;
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
public ItemContainer InputContainer
|
||||
@@ -25,7 +32,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 +91,177 @@ 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)
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
{
|
||||
// In multiplayer, the server handles the deconstruction into new items
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
}
|
||||
// doesn't quite work properly, remaining time changes if tinkering stops
|
||||
float deconstructionSpeedModifier = userDeconstructorSpeedMultiplier * (1f + tinkeringStrength * TinkeringSpeedIncrease);
|
||||
|
||||
if (targetItem.Prefab.RandomDeconstructionOutput)
|
||||
if (DeconstructItemsSimultaneously)
|
||||
{
|
||||
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);
|
||||
}
|
||||
deconstructTime += targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier);
|
||||
}
|
||||
else
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
if (progressTimer > deconstructTime)
|
||||
{
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
List<Item> items = inputContainer.Inventory.AllItems.ToList();
|
||||
foreach (Item targetItem in items)
|
||||
{
|
||||
CreateDeconstructProduct(deconstructProduct);
|
||||
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 != targetItem && (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;
|
||||
|
||||
}
|
||||
}
|
||||
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 * deconstructionSpeedModifier) : 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 (user != null && !user.Removed)
|
||||
{
|
||||
var abilityTargetItem = new AbilityItem(targetItem);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructed, abilityTargetItem);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.MaxCondition;
|
||||
|
||||
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)))
|
||||
{
|
||||
user.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
|
||||
foreach (Character character in Character.GetFriendlyCrew(user))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateDeconstructProduct(DeconstructItem deconstructProduct)
|
||||
int amount = 1;
|
||||
|
||||
if (user != null && !user.Removed)
|
||||
{
|
||||
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
|
||||
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
|
||||
var itemsCreated = new AbilityValueItem(amount, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedMaterial, itemsCreated);
|
||||
amount = (int)itemsCreated.Value;
|
||||
|
||||
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 * deconstructProduct.OutCondition;
|
||||
// used to spawn items directly into the deconstructor
|
||||
var itemContainer = new AbilityItemPrefabItem(item, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemContainer);
|
||||
}
|
||||
|
||||
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 +275,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 +307,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 +318,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 +338,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 +398,6 @@ namespace Barotrauma.Items.Components
|
||||
progressState = 0.0f;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
activateButton.Text = TextManager.Get(IsActive ? "DeconstructorCancel" : "DeconstructorDeconstruct");
|
||||
#endif
|
||||
|
||||
inputContainer.Inventory.Locked = IsActive;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private const float TinkeringForceIncrease = 1.5f;
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -113,31 +115,35 @@ namespace Barotrauma.Items.Components
|
||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
{
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||
float currForce = force * voltageFactor;
|
||||
float condition = item.Condition / item.MaxCondition;
|
||||
// Broken engine makes more noise.
|
||||
float noise = Math.Abs(currForce) * MathHelper.Lerp(1.5f, 1f, condition);
|
||||
UpdateAITargets(noise);
|
||||
//arbitrary multiplier that was added to changes in submarine mass without having to readjust all engines
|
||||
float forceMultiplier = 0.1f;
|
||||
if (User != null)
|
||||
{
|
||||
forceMultiplier *= MathHelper.Lerp(0.5f, 2.0f, (float)Math.Sqrt(User.GetSkillLevel("helm") / 100));
|
||||
}
|
||||
currForce *= maxForce * forceMultiplier;
|
||||
if (item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering)
|
||||
{
|
||||
currForce *= 1f + repairable.TinkeringStrength * TinkeringForceIncrease;
|
||||
}
|
||||
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||
Vector2 currForce = new Vector2(force * maxForce * forceMultiplier * voltageFactor, 0.0f);
|
||||
//less effective when in a bad condition
|
||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
|
||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, condition);
|
||||
if (item.Submarine.FlippedX) { currForce *= -1; }
|
||||
item.Submarine.ApplyForce(currForce);
|
||||
Vector2 forceVector = new Vector2(currForce, 0);
|
||||
item.Submarine.ApplyForce(forceVector);
|
||||
UpdatePropellerDamage(deltaTime);
|
||||
float maxChangeSpeed = 0.5f;
|
||||
float modifier = 2;
|
||||
float noise = MathUtils.NearlyEqual(0.0f, maxForce) ? 0.0f : currForce.Length() * forceMultiplier * modifier / maxForce;
|
||||
float min = Math.Max(1 - maxChangeSpeed, 0);
|
||||
float max = 1 + maxChangeSpeed;
|
||||
UpdateAITargets(Math.Clamp(noise, min, max), deltaTime);
|
||||
#if CLIENT
|
||||
particleTimer -= deltaTime;
|
||||
if (particleTimer <= 0.0f)
|
||||
{
|
||||
Vector2 particleVel = -currForce.ClampLength(5000.0f) / 5.0f;
|
||||
Vector2 particleVel = -forceVector.ClampLength(5000.0f) / 5.0f;
|
||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos * item.Scale,
|
||||
particleVel * Rand.Range(0.9f, 1.1f),
|
||||
0.0f, item.CurrentHull);
|
||||
@@ -147,14 +153,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAITargets(float increaseSpeed, float deltaTime)
|
||||
private void UpdateAITargets(float noise)
|
||||
{
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
item.AiTarget.IncreaseSoundRange(deltaTime, increaseSpeed);
|
||||
item.AiTarget.SoundRange = MathHelper.Lerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, noise / 100);
|
||||
if (item.CurrentHull != null && item.CurrentHull.AiTarget != null)
|
||||
{
|
||||
// It's possible that some othe item increases the hull's soundrange more than the engine.
|
||||
// It's possible that some other item increases the hull's soundrange more than the engine.
|
||||
item.CurrentHull.AiTarget.SoundRange = Math.Max(item.CurrentHull.AiTarget.SoundRange, item.AiTarget.SoundRange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(1.0f, true)]
|
||||
public float SkillRequirementMultiplier { get; set; }
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
private enum FabricatorState
|
||||
{
|
||||
Active = 1,
|
||||
@@ -240,7 +242,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem))
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
|
||||
{
|
||||
CancelFabricating();
|
||||
return;
|
||||
@@ -278,48 +281,91 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f);
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
{
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
}
|
||||
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
|
||||
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(Voltage, 1.0f);
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
if (timeUntilReady > 0.0f) { return; }
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems)
|
||||
{
|
||||
for (int i = 0; i < ingredient.Amount; i++)
|
||||
fabricatedItem.RequiredItems.ForEach(requiredItem => {
|
||||
for (int usedPrefabsAmount = 0; usedPrefabsAmount < requiredItem.Amount; usedPrefabsAmount++)
|
||||
{
|
||||
var availableItem = availableIngredients.FirstOrDefault(it =>
|
||||
it != null && ingredient.ItemPrefabs.Contains(it.Prefab) &&
|
||||
it.ConditionPercentage >= ingredient.MinCondition * 100.0f &&
|
||||
it.ConditionPercentage <= ingredient.MaxCondition * 100.0f);
|
||||
if (availableItem == null) { continue; }
|
||||
|
||||
if (ingredient.UseCondition && availableItem.ConditionPercentage - ingredient.MinCondition * 100 > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
availableItem.Condition -= availableItem.Prefab.Health * ingredient.MinCondition;
|
||||
continue;
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
return potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
});
|
||||
|
||||
if (availablePrefab == null) { continue; }
|
||||
|
||||
if (requiredItem.UseCondition && availablePrefab.ConditionPercentage - requiredItem.MinCondition * 100 > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
|
||||
{
|
||||
availablePrefab.Condition -= availablePrefab.Prefab.Health * requiredItem.MinCondition;
|
||||
continue;
|
||||
}
|
||||
|
||||
availablePrefabs.Remove(availablePrefab);
|
||||
Entity.Spawner.AddToRemoveQueue(availablePrefab);
|
||||
inputContainer.Inventory.RemoveItem(availablePrefab);
|
||||
}
|
||||
availableIngredients.Remove(availableItem);
|
||||
Entity.Spawner.AddToRemoveQueue(availableItem);
|
||||
inputContainer.Inventory.RemoveItem(availableItem);
|
||||
}
|
||||
});
|
||||
|
||||
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition * fabricatedItem.TargetItem.Health);
|
||||
|
||||
var fabricationValueItem = new AbilityValueItem(fabricatedItem.Amount, fabricatedItem.TargetItem);
|
||||
|
||||
int quality = 0;
|
||||
if (user?.Info != null)
|
||||
{
|
||||
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationValueItem);
|
||||
}
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
|
||||
|
||||
quality = GetFabricatedItemQuality(fabricatedItem, user);
|
||||
}
|
||||
|
||||
Character tempUser = user;
|
||||
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition * fabricatedItem.TargetItem.Health);
|
||||
for (int i = 0; i < fabricatedItem.Amount; i++)
|
||||
var tempUser = user;
|
||||
for (int i = 0; i < (int)fabricationValueItem.Value; i++)
|
||||
{
|
||||
float outCondition = fabricatedItem.OutCondition;
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition,
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
spawnedItem.Quality = quality;
|
||||
//reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers
|
||||
spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition,
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
spawnedItem.Quality = quality;
|
||||
//reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers
|
||||
spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,16 +379,19 @@ 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 AbilityValueString(0f, skill.Identifier);
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricationSkillGain, addedSkillValue);
|
||||
addedSkill += addedSkillValue.Value;
|
||||
|
||||
user.Info.IncreaseSkillLevel(
|
||||
skill.Identifier,
|
||||
skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f),
|
||||
user.Position + Vector2.UnitY * 150.0f);
|
||||
addedSkill);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,26 +412,57 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateRequiredTimeProjSpecific();
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem)
|
||||
private int GetFabricatedItemQuality(FabricationRecipe fabricatedItem, Character user)
|
||||
{
|
||||
if (fabricableItem == null) { return false; }
|
||||
List<Item> availableIngredients = GetAvailableIngredients();
|
||||
return CanBeFabricated(fabricableItem, availableIngredients);
|
||||
if (user == null) { return 0; }
|
||||
if (fabricatedItem.TargetItem.ConfigElement.GetChildElement("Quality") == null) { return 0; }
|
||||
int quality = 0;
|
||||
float floatQuality = 0.0f;
|
||||
foreach (string tag in fabricatedItem.TargetItem.Tags)
|
||||
{
|
||||
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
|
||||
}
|
||||
quality = (int)floatQuality;
|
||||
|
||||
const int MaxCraftingSkill = 100;
|
||||
|
||||
quality += fabricatedItem.RequiredSkills.All(s => user.GetSkillLevel(s.Identifier) >= MaxCraftingSkill) ? 1 : 0;
|
||||
quality += FabricationDegreeOfSuccess(user, fabricatedItem.RequiredSkills) >= 0.5f ? 1 : 0;
|
||||
return quality;
|
||||
}
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, IEnumerable<Item> availableIngredients)
|
||||
partial void UpdateRequiredTimeProjSpecific();
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, Dictionary<string, List<Item>> availableIngredients, Character character)
|
||||
{
|
||||
if (fabricableItem == null) { return false; }
|
||||
foreach (FabricationRecipe.RequiredItem requiredItem in fabricableItem.RequiredItems)
|
||||
if (fabricableItem == null) { return false; }
|
||||
if (fabricableItem.RequiresRecipe && (character == null || !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier))) { return false; }
|
||||
|
||||
return fabricableItem.RequiredItems.All(requiredItem =>
|
||||
{
|
||||
if (availableIngredients.Count(it => IsItemValidIngredient(it, requiredItem)) < requiredItem.Amount)
|
||||
int availablePrefabsAmount = 0;
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
return false;
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
foreach (Item availablePrefab in availablePrefabs)
|
||||
{
|
||||
if (availablePrefab.Condition / availablePrefab.Prefab.Health >= requiredItem.MinCondition &&
|
||||
availablePrefab.Condition / availablePrefab.Prefab.Health <= requiredItem.MaxCondition)
|
||||
{
|
||||
availablePrefabsAmount++;
|
||||
}
|
||||
|
||||
if (availablePrefabsAmount >= requiredItem.Amount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private float GetRequiredTime(FabricationRecipe fabricableItem, Character user)
|
||||
@@ -416,7 +496,7 @@ namespace Barotrauma.Items.Components
|
||||
/// Get a list of all items available in the input container and linked containers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<Item> GetAvailableIngredients()
|
||||
private Dictionary<string, List<Item>> GetAvailableIngredients()
|
||||
{
|
||||
List<Item> availableIngredients = new List<Item>();
|
||||
availableIngredients.AddRange(inputContainer.Inventory.AllItems);
|
||||
@@ -448,7 +528,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
return availableIngredients;
|
||||
Dictionary<string, List<Item>> ingredientsDictionary = new Dictionary<string, List<Item>>();
|
||||
for (int i = 0; i < availableIngredients.Count; i++)
|
||||
{
|
||||
var itemIdentifier = availableIngredients[i].prefab.Identifier;
|
||||
if (!ingredientsDictionary.ContainsKey(itemIdentifier))
|
||||
{
|
||||
ingredientsDictionary[itemIdentifier] = new List<Item>(availableIngredients.Count);
|
||||
}
|
||||
|
||||
ingredientsDictionary[itemIdentifier].Add(availableIngredients[i]);
|
||||
}
|
||||
|
||||
return ingredientsDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -463,40 +555,41 @@ namespace Barotrauma.Items.Components
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
foreach (var requiredItem in targetItem.RequiredItems)
|
||||
{
|
||||
targetItem.RequiredItems.ForEach(requiredItem => {
|
||||
for (int i = 0; i < requiredItem.Amount; i++)
|
||||
{
|
||||
var matchingItem = availableIngredients.Find(it => !usedItems.Contains(it) && IsItemValidIngredient(it, requiredItem));
|
||||
if (matchingItem == null) { continue; }
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
availableIngredients.Remove(matchingItem);
|
||||
|
||||
if (matchingItem.ParentInventory == inputContainer.Inventory)
|
||||
{
|
||||
//already in input container, all good
|
||||
usedItems.Add(matchingItem);
|
||||
}
|
||||
else //in another inventory, we need to move the item
|
||||
{
|
||||
if (!inputContainer.Inventory.CanBePut(matchingItem))
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
unneededItem?.Drop(null, createNetworkEvent: !isClient);
|
||||
}
|
||||
inputContainer.Inventory.TryPutItem(matchingItem, user: null, createNetworkEvent: !isClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !usedItems.Contains(potentialPrefab) &&
|
||||
potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
});
|
||||
if (availablePrefab == null) { continue; }
|
||||
|
||||
private bool IsItemValidIngredient(Item item, FabricationRecipe.RequiredItem requiredItem)
|
||||
{
|
||||
return
|
||||
item != null &&
|
||||
requiredItem.ItemPrefabs.Contains(item.prefab) &&
|
||||
item.Condition / item.Prefab.Health >= requiredItem.MinCondition &&
|
||||
item.Condition / item.Prefab.Health <= requiredItem.MaxCondition;
|
||||
availablePrefabs.Remove(availablePrefab);
|
||||
|
||||
if (availablePrefab.ParentInventory == inputContainer.Inventory)
|
||||
{
|
||||
//already in input container, all good
|
||||
usedItems.Add(availablePrefab);
|
||||
}
|
||||
else //in another inventory, we need to move the item
|
||||
{
|
||||
if (!inputContainer.Inventory.CanBePut(availablePrefab))
|
||||
{
|
||||
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
unneededItem?.Drop(null, createNetworkEvent: !isClient);
|
||||
}
|
||||
inputContainer.Inventory.TryPutItem(availablePrefab, user: null, createNetworkEvent: !isClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
|
||||
@@ -7,10 +7,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class MiniMap : Powered
|
||||
{
|
||||
class HullData
|
||||
internal class HullData
|
||||
{
|
||||
public float? Oxygen;
|
||||
public float? Water;
|
||||
public float? HullOxygenAmount,
|
||||
HullWaterAmount;
|
||||
|
||||
public float? ReceivedOxygenAmount,
|
||||
ReceivedWaterAmount;
|
||||
|
||||
public readonly HashSet<IdCard> Cards = new HashSet<IdCard>();
|
||||
|
||||
public bool Distort;
|
||||
public float DistortionTimer;
|
||||
@@ -45,17 +50,38 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable hull status mode.")]
|
||||
public bool EnableHullStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable electrical view mode.")]
|
||||
public bool EnableElectricalView
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
|
||||
public bool EnableItemFinder
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MiniMap(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
InitProjSpecific(element);
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//periodically reset all hull data
|
||||
//(so that outdated hull info won't be shown if detectors stop sending signals)
|
||||
@@ -65,13 +91,29 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!hullData.Distort)
|
||||
{
|
||||
hullData.Oxygen = null;
|
||||
hullData.Water = null;
|
||||
hullData.ReceivedOxygenAmount = null;
|
||||
hullData.ReceivedWaterAmount = null;
|
||||
}
|
||||
}
|
||||
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (cardRefreshTimer > cardRefreshDelay)
|
||||
{
|
||||
if (item.Submarine is { } sub)
|
||||
{
|
||||
UpdateIDCards(sub);
|
||||
}
|
||||
|
||||
cardRefreshTimer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cardRefreshTimer += deltaTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
@@ -81,7 +123,7 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
@@ -99,30 +141,49 @@ namespace Barotrauma.Items.Components
|
||||
hullDatas.Add(sourceHull, hullData);
|
||||
}
|
||||
|
||||
if (hullData.Distort) return;
|
||||
if (hullData.Distort) { return; }
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "water_data_in":
|
||||
//cheating a bit because water detectors don't actually send the water level
|
||||
float waterAmount;
|
||||
if (source.GetComponent<WaterDetector>() == null)
|
||||
{
|
||||
hullData.Water = Rand.Range(0.0f, 1.0f);
|
||||
waterAmount = Rand.Range(0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hullData.Water = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
waterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
}
|
||||
hullData.ReceivedWaterAmount = waterAmount;
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
if (!(linked is Hull linkedHull)) { continue; }
|
||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
||||
{
|
||||
linkedHullData = new HullData();
|
||||
hullDatas.Add(linkedHull, linkedHullData);
|
||||
}
|
||||
linkedHullData.ReceivedWaterAmount = waterAmount;
|
||||
}
|
||||
break;
|
||||
case "oxygen_data_in":
|
||||
float oxy;
|
||||
|
||||
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out oxy))
|
||||
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
|
||||
{
|
||||
oxy = Rand.Range(0.0f, 100.0f);
|
||||
}
|
||||
|
||||
hullData.Oxygen = oxy;
|
||||
hullData.ReceivedOxygenAmount = oxy;
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
if (!(linked is Hull linkedHull)) { continue; }
|
||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
||||
{
|
||||
linkedHullData = new HullData();
|
||||
hullDatas.Add(linkedHull, linkedHullData);
|
||||
}
|
||||
linkedHullData.ReceivedOxygenAmount = oxy;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-14
@@ -11,9 +11,12 @@ namespace Barotrauma.Items.Components
|
||||
private float generatedAmount;
|
||||
|
||||
//key = vent, float = total volume of the hull the vent is in and the hulls connected to it
|
||||
private Dictionary<Vent, float> ventList;
|
||||
private List<(Vent vent, float hullVolume)> ventList;
|
||||
|
||||
private float totalHullVolume;
|
||||
|
||||
private float ventUpdateTimer;
|
||||
const float VentUpdateInterval = 5.0f;
|
||||
|
||||
public float CurrFlow
|
||||
{
|
||||
@@ -31,6 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
public OxygenGenerator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//randomize update timer so all oxygen generators don't update at the same time
|
||||
ventUpdateTimer = Rand.Range(0.0f, VentUpdateInterval);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -64,7 +69,7 @@ namespace Barotrauma.Items.Components
|
||||
//20% condition = 4%
|
||||
CurrFlow *= conditionMult * conditionMult;
|
||||
|
||||
UpdateVents(CurrFlow);
|
||||
UpdateVents(CurrFlow, deltaTime);
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -75,7 +80,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
ventList = new Dictionary<Vent, float>();
|
||||
totalHullVolume = 0.0f;
|
||||
ventList ??= new List<(Vent vent, float hullVolume)>();
|
||||
ventList.Clear();
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
{
|
||||
if (!(entity is Item linkedItem)) { continue; }
|
||||
@@ -83,30 +90,40 @@ namespace Barotrauma.Items.Components
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
if (vent?.Item.CurrentHull == null) { continue; }
|
||||
|
||||
ventList.Add(vent, 0.0f);
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: true, searchDepth: 10, ignoreClosedGaps: true))
|
||||
{
|
||||
totalHullVolume += vent.Item.CurrentHull.Volume;
|
||||
ventList.Add((vent, vent.Item.CurrentHull.Volume));
|
||||
}
|
||||
|
||||
for (int i = 0; i < ventList.Count; i++)
|
||||
{
|
||||
Vent vent = ventList[i].vent;
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 3, ignoreClosedGaps: true))
|
||||
{
|
||||
//another vent in the connected hull -> don't add it to this vent's total hull volume
|
||||
if (ventList.Any(v => v.vent != vent && v.vent.Item.CurrentHull == connectedHull)) { continue; }
|
||||
totalHullVolume += connectedHull.Volume;
|
||||
ventList[vent] += connectedHull.Volume;
|
||||
ventList[i] = (ventList[i].vent, ventList[i].hullVolume + connectedHull.Volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVents(float deltaOxygen)
|
||||
|
||||
private void UpdateVents(float deltaOxygen, float deltaTime)
|
||||
{
|
||||
if (ventList == null)
|
||||
if (ventList == null || ventUpdateTimer < 0.0f)
|
||||
{
|
||||
GetVents();
|
||||
ventUpdateTimer = VentUpdateInterval;
|
||||
}
|
||||
ventUpdateTimer -= deltaTime;
|
||||
|
||||
if (!ventList.Any() || totalHullVolume <= 0.0f) { return; }
|
||||
|
||||
foreach (KeyValuePair<Vent, float> v in ventList)
|
||||
foreach ((Vent vent, float hullVolume) in ventList)
|
||||
{
|
||||
if (v.Key?.Item.CurrentHull == null) { continue; }
|
||||
if (vent.Item.CurrentHull == null) { continue; }
|
||||
|
||||
v.Key.OxygenFlow = deltaOxygen * (v.Value / totalHullVolume);
|
||||
v.Key.IsActive = true;
|
||||
vent.OxygenFlow = deltaOxygen * (hullVolume / totalHullVolume);
|
||||
vent.IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace Barotrauma.Items.Components
|
||||
public bool HasPower => IsActive && Voltage >= MinVoltage;
|
||||
public bool IsAutoControlled => pumpSpeedLockTimer > 0.0f || isActiveLockTimer > 0.0f;
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -105,11 +107,19 @@ namespace Barotrauma.Items.Components
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
|
||||
if (item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering)
|
||||
{
|
||||
currFlow *= 1f + repairable.TinkeringStrength * TinkeringSpeedIncrease;
|
||||
}
|
||||
|
||||
//less effective when in a bad condition
|
||||
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
item.CurrentHull.WaterVolume += currFlow;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
||||
|
||||
Voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)
|
||||
|
||||
@@ -367,23 +367,19 @@ namespace Barotrauma.Items.Components
|
||||
item.Condition -= fissionRate / 100.0f * fuelConsumptionRate * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
var aiTarget = item.CurrentHull.AiTarget;
|
||||
if (aiTarget != null && MaxPowerOutput > 0)
|
||||
{
|
||||
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
|
||||
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
|
||||
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, noise);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.AiTarget != null && MaxPowerOutput > 0)
|
||||
{
|
||||
var aiTarget = item.AiTarget;
|
||||
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
|
||||
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
var hullAITarget = item.CurrentHull.AiTarget;
|
||||
if (hullAITarget != null)
|
||||
{
|
||||
hullAITarget.SoundRange = Math.Max(hullAITarget.SoundRange, aiTarget.SoundRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,15 +494,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float prevFireTimer = fireTimer;
|
||||
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
|
||||
|
||||
#if SERVER
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedConstruction == item)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(blameOnBroken.Character, deltaTime);
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(item, blameOnBroken.Character, deltaTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fireTimer >= FireDelay && prevFireTimer < fireDelay)
|
||||
{
|
||||
new FireSource(item.WorldPosition);
|
||||
@@ -595,7 +588,7 @@ namespace Barotrauma.Items.Components
|
||||
GameServer.Log("Reactor meltdown!", ServerLog.MessageType.ItemInteraction);
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnReactorMeltdown(blameOnBroken?.Character);
|
||||
GameMain.Server.KarmaManager.OnReactorMeltdown(item, blameOnBroken?.Character);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -364,6 +364,16 @@ namespace Barotrauma.Items.Components
|
||||
float velY = MathHelper.Lerp((neutralBallastLevel * 100 - 50) * 2, -100 * Math.Sign(targetVelocity.Y), Math.Abs(targetVelocity.Y) / 100.0f);
|
||||
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.
|
||||
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 * Physics.DisplayToRealWorldRatio).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
|
||||
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_y");
|
||||
}
|
||||
|
||||
// if our tactical AI pilot has left, revert back to maintaining position
|
||||
if (navigateTactically && (user == null || user.SelectedConstruction != item))
|
||||
{
|
||||
@@ -382,8 +392,7 @@ namespace Barotrauma.Items.Components
|
||||
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
|
||||
user.Info.IncreaseSkillLevel(
|
||||
"helm",
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime,
|
||||
user.Position + Vector2.UnitY * 150.0f);
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime);
|
||||
}
|
||||
|
||||
private void UpdateAutoPilot(float deltaTime)
|
||||
|
||||
Reference in New Issue
Block a user