Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
public string Name => LimbType.ToString();
public Dictionary<string, SerializableProperty> SerializableProperties => null;
public Dictionary<Identifier, SerializableProperty> SerializableProperties => null;
public LimbPos(LimbType limbType, Vector2 position, bool allowUsingLimb)
{
@@ -61,21 +61,21 @@ namespace Barotrauma.Items.Components
public IEnumerable<LimbPos> LimbPositions { get { return limbPositions; } }
[Editable, Serialize(false, false, description: "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.", alwaysUseInstanceValues: true)]
[Editable, Serialize(false, IsPropertySaveable.No, description: "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.", alwaysUseInstanceValues: true)]
public bool IsToggle
{
get;
set;
}
[Editable, Serialize(false, false, description: "Whether the item is toggled on/off. Only valid if IsToggle is set to true.", alwaysUseInstanceValues: true)]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Whether the item is toggled on/off. Only valid if IsToggle is set to true.", alwaysUseInstanceValues: true)]
public bool State
{
get;
set;
}
[Serialize(true, false, description: "Should the HUD (inventory, health bar, etc) be hidden when this item is selected.")]
[Serialize(true, IsPropertySaveable.No, description: "Should the HUD (inventory, health bar, etc) be hidden when this item is selected.")]
public bool HideHUD
{
get;
@@ -87,10 +87,10 @@ namespace Barotrauma.Items.Components
Air, Water, Both
};
[Serialize(UseEnvironment.Both, false, description: "Can the item be selected in air, underwater or both.")]
[Serialize(UseEnvironment.Both, IsPropertySaveable.No, description: "Can the item be selected in air, underwater or both.")]
public UseEnvironment UsableIn { get; set; }
[Serialize(false, false, description: "Should the character using the item be drawn behind the item.")]
[Serialize(false, IsPropertySaveable.No, description: "Should the character using the item be drawn behind the item.")]
public bool DrawUserBehind
{
get;
@@ -114,7 +114,7 @@ namespace Barotrauma.Items.Components
private set;
} = true;
public Controller(Item item, XElement element)
public Controller(Item item, ContentXElement element)
: base(item, element)
{
limbPositions = new List<LimbPos>();
@@ -123,7 +123,7 @@ namespace Barotrauma.Items.Components
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (subElement.Name != "limbposition") { continue; }
string limbStr = subElement.GetAttributeString("limb", "");
@@ -33,15 +33,15 @@ namespace Barotrauma.Items.Components
get { return outputContainer; }
}
[Serialize(false, true)]
[Serialize(false, IsPropertySaveable.Yes)]
public bool DeconstructItemsSimultaneously { get; set; }
[Editable, Serialize(1.0f, true)]
[Editable, Serialize(1.0f, IsPropertySaveable.Yes)]
public float DeconstructionSpeed { get; set; }
public override bool RecreateGUIOnResolutionChange => true;
public Deconstructor(Item item, XElement element)
public Deconstructor(Item item, ContentXElement element)
: base(item, element)
{
InitProjSpecific(element);
@@ -88,8 +88,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
progressTimer += deltaTime * Math.Min(Voltage, 1.0f);
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, 1.0f);
float tinkeringStrength = 0f;
if (repairable.IsTinkering)
@@ -114,9 +113,9 @@ namespace Barotrauma.Items.Components
foreach (Item targetItem in items)
{
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))))));
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) &&
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier == r))))).ToList();
ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
}
@@ -133,8 +132,8 @@ namespace Barotrauma.Items.Components
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)));
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)).ToList();
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
@@ -230,7 +229,7 @@ namespace Barotrauma.Items.Components
foreach (Item otherItem in inputItems)
{
if (targetItem == otherItem) { continue; }
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r.Equals(otherItem.Prefab.Identifier, StringComparison.OrdinalIgnoreCase)))
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r == otherItem.Prefab.Identifier))
{
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
foreach (Character character in Character.GetFriendlyCrew(user))
@@ -246,14 +245,14 @@ namespace Barotrauma.Items.Components
{
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddToRemoveQueue(otherItem);
Entity.Spawner.AddItemToRemoveQueue(otherItem);
}
allowRemove = false;
return;
}
inputContainer.Inventory.RemoveItem(otherItem);
OutputContainer.Inventory.RemoveItem(otherItem);
Entity.Spawner.AddToRemoveQueue(otherItem);
Entity.Spawner.AddItemToRemoveQueue(otherItem);
}
}
}
@@ -268,7 +267,7 @@ namespace Barotrauma.Items.Components
int amount = (int)amountMultiplier;
for (int i = 0; i < amount; i++)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
{
spawnedItem.StolenDuringRound = targetItem.StolenDuringRound;
spawnedItem.AllowStealing = targetItem.AllowStealing;
@@ -300,7 +299,7 @@ namespace Barotrauma.Items.Components
}
}
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + targetItem.prefab.Identifier);
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + targetItem.Prefab.Identifier);
if (targetItem.AllowDeconstruct && allowRemove)
{
@@ -314,7 +313,7 @@ namespace Barotrauma.Items.Components
}
}
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
Entity.Spawner.AddItemToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
@@ -392,16 +391,16 @@ namespace Barotrauma.Items.Components
{
if (deconstructItem.RequiredDeconstructor.Length > 0)
{
if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) { continue; }
if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) { 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; }
if (!deconstructItem.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier == r))) { 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; }
if (!deconstructItem.RequiredOtherItem.Any(r => otherInputItem.HasTag(r) || otherInputItem.Prefab.Identifier == r)) { continue; }
var geneticMaterial1 = inputItem.GetComponent<GeneticMaterial>();
var geneticMaterial2 = otherInputItem.GetComponent<GeneticMaterial>();
@@ -427,7 +426,7 @@ namespace Barotrauma.Items.Components
if (inputContainer.Inventory.IsEmpty()) { active = false; }
IsActive = active;
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
//currPowerConsumption = IsActive ? powerConsumption : 0.0f;
userDeconstructorSpeedMultiplier = user != null ? 1f + user.GetStatValue(StatTypes.DeconstructorSpeedMultiplier) : 1f;
#if SERVER
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
public Character User;
[Editable(0.0f, 10000000.0f),
Serialize(500.0f, true, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
Serialize(500.0f, IsPropertySaveable.Yes, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
public float MaxForce
{
get { return maxForce; }
@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
}
}
[Editable, Serialize("0.0,0.0", true,
[Editable, Serialize("0.0,0.0", IsPropertySaveable.Yes,
description: "The position of the propeller as an offset from the item's center (in pixels)."+
" Determines where the particles spawn and the position that causes characters to take damage from the engine if the PropellerDamage is defined.")]
public Vector2 PropellerPos
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
set;
}
[Editable, Serialize(false, true)]
[Editable, Serialize(false, IsPropertySaveable.Yes)]
public bool DisablePropellerDamage
{
get;
@@ -75,12 +75,12 @@ namespace Barotrauma.Items.Components
private const float TinkeringForceIncrease = 1.5f;
public Engine(Item item, XElement element)
public Engine(Item item, ContentXElement element)
: base(item, element)
{
IsActive = true;
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Update(float deltaTime, Camera cam)
{
@@ -103,14 +103,16 @@ namespace Barotrauma.Items.Components
controlLockTimer -= deltaTime;
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
//engines consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (powerConsumption == 0.0f)
{
prevVoltage = 1;
hasPower = true;
}
else
{
hasPower = Voltage > MinVoltage;
}
if (powerConsumption == 0.0f) { Voltage = 1.0f; }
prevVoltage = Voltage;
hasPower = Voltage > MinVoltage;
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
if (Math.Abs(Force) > 1.0f)
@@ -154,6 +156,33 @@ namespace Barotrauma.Items.Components
}
}
/// <summary>
/// Power consumption of the engine. Only consume power when active and adjust consumption based on condition and target force.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
if (connection != this.powerIn)
{
return 0;
}
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
//engines consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
return currPowerConsumption;
}
/// <summary>
/// When grid is resolved update the previous voltage
/// </summary>
public override void GridResolved(Connection connection)
{
if (connection == powerIn)
{
prevVoltage = Voltage;
}
}
private void UpdateAITargets(float noise)
{
if (item.AiTarget != null)
@@ -1,7 +1,9 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -11,7 +13,7 @@ namespace Barotrauma.Items.Components
{
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
{
private readonly List<FabricationRecipe> fabricationRecipes = new List<FabricationRecipe>();
private ImmutableDictionary<uint, FabricationRecipe> fabricationRecipes; //this is not readonly because tutorials fuck this up!!!!
private FabricationRecipe fabricatedItem;
private float timeUntilReady;
@@ -20,7 +22,7 @@ namespace Barotrauma.Items.Components
private string savedFabricatedItem;
private float savedTimeUntilReady, savedRequiredTime;
private readonly Dictionary<string, List<Item>> availableIngredients = new Dictionary<string, List<Item>>();
private readonly Dictionary<Identifier, List<Item>> availableIngredients = new Dictionary<Identifier, List<Item>>();
const float RefreshIngredientsInterval = 1.0f;
private float refreshIngredientsTimer;
@@ -31,10 +33,10 @@ namespace Barotrauma.Items.Components
private ItemContainer inputContainer, outputContainer;
[Serialize(1.0f, true)]
[Serialize(1.0f, IsPropertySaveable.Yes)]
public float FabricationSpeed { get; set; }
[Serialize(1.0f, true)]
[Serialize(1.0f, IsPropertySaveable.Yes)]
public float SkillRequirementMultiplier { get; set; }
private const float TinkeringSpeedIncrease = 2.5f;
@@ -78,10 +80,10 @@ namespace Barotrauma.Items.Components
private float progressState;
public Fabricator(Item item, XElement element)
public Fabricator(Item item, ContentXElement element)
: base(item, element)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("fabricableitem", StringComparison.OrdinalIgnoreCase))
{
@@ -90,26 +92,22 @@ namespace Barotrauma.Items.Components
}
}
var fabricationRecipes = new Dictionary<uint, FabricationRecipe>();
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
{
foreach (FabricationRecipe recipe in itemPrefab.FabricationRecipes)
foreach (FabricationRecipe recipe in itemPrefab.FabricationRecipes.Values)
{
if (recipe.SuitableFabricatorIdentifiers.Length > 0)
{
if (!recipe.SuitableFabricatorIdentifiers.Any(i => item.prefab.Identifier == i || item.HasTag(i)))
if (!recipe.SuitableFabricatorIdentifiers.Any(i => item.Prefab.Identifier == i || item.HasTag(i)))
{
continue;
}
}
fabricationRecipes.Add(recipe);
fabricationRecipes.Add(recipe.RecipeHash, recipe);
}
}
fabricationRecipes.Sort((r1, r2) =>
{
int hash1 = (int)r1.TargetItem.UIntIdentifier;
int hash2 = (int)r2.TargetItem.UIntIdentifier;
return hash1 - hash2;
});
this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
state = FabricatorState.Stopped;
@@ -129,9 +127,9 @@ namespace Barotrauma.Items.Components
inputContainer = containers[0];
outputContainer = containers[1];
foreach (var recipe in fabricationRecipes)
foreach (var recipe in fabricationRecipes.Values)
{
if (recipe.RequiredItems.Count > inputContainer.Capacity)
if (recipe.RequiredItems.Length > inputContainer.Capacity)
{
DebugConsole.ThrowError("Error in item \"" + item.Name + "\": There's not enough room in the input inventory for the ingredients of \"" + recipe.TargetItem.Name + "\"!");
}
@@ -158,16 +156,11 @@ namespace Barotrauma.Items.Components
return picker != null;
}
public void RemoveFabricationRecipes(List<string> allowedIdentifiers)
public void RemoveFabricationRecipes(IEnumerable<Identifier> allowedIdentifiers)
{
for (int i = 0; i < fabricationRecipes.Count; i++)
{
if (!allowedIdentifiers.Contains(fabricationRecipes[i].TargetItem.Identifier))
{
fabricationRecipes.RemoveAt(i);
i--;
}
}
fabricationRecipes = fabricationRecipes
.Where(kvp => allowedIdentifiers.Contains(kvp.Value.TargetItemPrefabIdentifier))
.ToImmutableDictionary();
CreateRecipes();
}
@@ -201,9 +194,6 @@ namespace Barotrauma.Items.Components
inputContainer.Inventory.Locked = true;
outputContainer.Inventory.Locked = true;
currPowerConsumption = powerConsumption;
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (GameMain.NetworkMember?.IsServer ?? true)
{
State = FabricatorState.Active;
@@ -211,7 +201,7 @@ namespace Barotrauma.Items.Components
#if SERVER
if (user != null && addToServerLog)
{
GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName.Value + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#endif
}
@@ -237,7 +227,7 @@ namespace Barotrauma.Items.Components
#if SERVER
if (user != null)
{
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName.Value + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#elif CLIENT
itemList.Enabled = true;
@@ -304,11 +294,10 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (powerConsumption <= 0) { Voltage = 1.0f; }
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(Voltage, 1.0f);
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, 1.0f);
UpdateRequiredTimeProjSpecific();
@@ -370,7 +359,7 @@ namespace Barotrauma.Items.Components
}
availableItems.Remove(availableItem);
Entity.Spawner.AddToRemoveQueue(availableItem);
Entity.Spawner.AddItemToRemoveQueue(availableItem);
inputContainer.Inventory.RemoveItem(availableItem);
break;
}
@@ -389,7 +378,7 @@ namespace Barotrauma.Items.Components
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
quality = GetFabricatedItemQuality(fabricatedItem, user);
}
@@ -397,10 +386,10 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
{
float outCondition = fabricatedItem.OutCondition;
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
if (i < amountFittingContainer)
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
@@ -413,7 +402,7 @@ namespace Barotrauma.Items.Components
}
else
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
@@ -469,13 +458,30 @@ namespace Barotrauma.Items.Components
}
/// <summary>
/// Power consumption of the fabricator. Only consume power when active and adjust consumption based on condition.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
//No consumption if not powerin or is off
if (connection != this.powerIn || !IsActive)
{
return 0;
}
currPowerConsumption = PowerConsumption;
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
return currPowerConsumption;
}
private int GetFabricatedItemQuality(FabricationRecipe fabricatedItem, Character user)
{
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)
foreach (var tag in fabricatedItem.TargetItem.Tags)
{
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
}
@@ -489,8 +495,8 @@ namespace Barotrauma.Items.Components
}
partial void UpdateRequiredTimeProjSpecific();
private bool CanBeFabricated(FabricationRecipe fabricableItem, Dictionary<string, List<Item>> availableIngredients, Character character)
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
{
if (fabricableItem == null) { return false; }
if (fabricableItem.RequiresRecipe && (character == null || !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier))) { return false; }
@@ -533,13 +539,13 @@ namespace Barotrauma.Items.Components
return fabricableItem.RequiredTime / FabricationSpeed / MathHelper.Clamp(t, 0.01f, 2.0f);
}
public float FabricationDegreeOfSuccess(Character character, List<Skill> skills)
public float FabricationDegreeOfSuccess(Character character, ImmutableArray<Skill> skills)
{
if (skills.Count == 0) { return 1.0f; }
if (skills.Length == 0) { return 1.0f; }
if (character == null) { return 0.0f; }
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
float average = skillSum / skills.Count;
float average = skillSum / skills.Length;
return (average + 100.0f) / 2.0f / 100.0f;
}
@@ -593,7 +599,7 @@ namespace Barotrauma.Items.Components
availableIngredients.Clear();
foreach (Item item in itemList)
{
var itemIdentifier = item.prefab.Identifier;
var itemIdentifier = item.Prefab.Identifier;
if (!availableIngredients.ContainsKey(itemIdentifier))
{
availableIngredients[itemIdentifier] = new List<Item>(itemList.Count);
@@ -663,7 +669,7 @@ namespace Barotrauma.Items.Components
return componentElement;
}
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
{
base.Load(componentElement, usePrefabValues, idRemap);
savedFabricatedItem = componentElement.GetAttributeString("fabricateditemidentifier", "");
@@ -678,7 +684,7 @@ namespace Barotrauma.Items.Components
inputContainer?.OnMapLoaded();
outputContainer?.OnMapLoaded();
var recipe = fabricationRecipes.Find(r => r.TargetItem.Identifier == savedFabricatedItem);
var recipe = fabricationRecipes.Values.FirstOrDefault(r => r.TargetItem.Identifier == savedFabricatedItem);
if (recipe == null)
{
DebugConsole.ThrowError("Error while loading a fabricator. Can't continue fabricating \"" + savedFabricatedItem + "\" (matching recipe not found).");
@@ -696,13 +702,13 @@ namespace Barotrauma.Items.Components
}
class AbilityFabricatorSkillGain : AbilityObject, IAbilityValue, IAbilitySkillIdentifier
{
public AbilityFabricatorSkillGain(string skillIdentifier, float skillAmount)
public AbilityFabricatorSkillGain(Identifier skillIdentifier, float skillAmount)
{
SkillIdentifier = skillIdentifier;
Value = skillAmount;
}
public float Value { get; set; }
public string SkillIdentifier { get; set; }
public Identifier SkillIdentifier { get; set; }
}
class AbilityFabricationItemAmount : AbilityObject, IAbilityValue, IAbilityItemPrefab
@@ -29,49 +29,49 @@ namespace Barotrauma.Items.Components
private readonly Dictionary<Hull, HullData> hullDatas;
[Editable, Serialize(false, true, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
public bool RequireWaterDetectors
{
get;
set;
}
[Editable, Serialize(true, true, description: "Does the machine require inputs from oxygen detectors in order to show the oxygen levels inside rooms.")]
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Does the machine require inputs from oxygen detectors in order to show the oxygen levels inside rooms.")]
public bool RequireOxygenDetectors
{
get;
set;
}
[Editable, Serialize(true, true, description: "Should damaged walls be displayed by the machine.")]
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should damaged walls be displayed by the machine.")]
public bool ShowHullIntegrity
{
get;
set;
}
[Editable, Serialize(true, true, description: "Enable hull status mode.")]
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable hull status mode.")]
public bool EnableHullStatus
{
get;
set;
}
[Editable, Serialize(true, true, description: "Enable electrical view mode.")]
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable electrical view mode.")]
public bool EnableElectricalView
{
get;
set;
}
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable item finder mode.")]
public bool EnableItemFinder
{
get;
set;
}
public MiniMap(Item item, XElement element)
public MiniMap(Item item, ContentXElement element)
: base(item, element)
{
IsActive = true;
@@ -114,9 +114,6 @@ namespace Barotrauma.Items.Components
}
#endif
currPowerConsumption = powerConsumption;
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
hasPower = Voltage > MinVoltage;
if (hasPower)
{
@@ -124,6 +121,19 @@ namespace Barotrauma.Items.Components
}
}
/// <summary>
/// Power consumption of the MiniMap. Only consume power when active and adjust consumption based on condition.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
if (connection != powerIn || !IsActive)
{
return 0;
}
return PowerConsumption * MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
}
public override bool Pick(Character picker)
{
return picker != null;
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
{
partial class OutpostTerminal : ItemComponent
{
public OutpostTerminal(Item item, XElement element) : base(item, element)
public OutpostTerminal(Item item, ContentXElement element) : base(item, element)
{
InitProjSpecific(element);
}
@@ -24,14 +24,14 @@ namespace Barotrauma.Items.Components
private set;
}
[Editable, Serialize(400.0f, true, description: "How much oxygen the machine generates when operating at full power.", alwaysUseInstanceValues: true)]
[Editable, Serialize(400.0f, IsPropertySaveable.Yes, description: "How much oxygen the machine generates when operating at full power.", alwaysUseInstanceValues: true)]
public float GeneratedAmount
{
get { return generatedAmount; }
set { generatedAmount = MathHelper.Clamp(value, -10000.0f, 10000.0f); }
}
public OxygenGenerator(Item item, XElement element)
public OxygenGenerator(Item item, ContentXElement element)
: base(item, element)
{
//randomize update timer so all oxygen generators don't update at the same time
@@ -44,25 +44,15 @@ namespace Barotrauma.Items.Components
UpdateOnActiveEffects(deltaTime);
CurrFlow = 0.0f;
currPowerConsumption = powerConsumption;
//consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (powerConsumption <= 0.0f)
{
Voltage = 1.0f;
}
if (item.CurrentHull == null) { return; }
if (Voltage < MinVoltage)
if (Voltage < MinVoltage && PowerConsumption > 0)
{
return;
}
CurrFlow = Math.Min(Voltage, 1.0f) * generatedAmount * 100.0f;
//less effective when in bad condition
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, 1.0f) * generatedAmount * 100.0f;
float conditionMult = item.Condition / item.MaxCondition;
//100% condition = 100% oxygen
//50% condition = 25% oxygen
@@ -72,6 +62,23 @@ namespace Barotrauma.Items.Components
UpdateVents(CurrFlow, deltaTime);
}
/// <summary>
/// Power consumption of the Oxygen Generator. Only consume power when active and adjust consumption based on condition.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
if (connection != this.powerIn)
{
return 0;
}
float consumption = powerConsumption;
//consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref consumption);
return consumption;
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
base.UpdateBroken(deltaTime, cam);
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
private float pumpSpeedLockTimer, isActiveLockTimer;
[Serialize(0.0f, true, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
public float FlowPercentage
{
get { return flowPercentage; }
@@ -48,18 +48,18 @@ namespace Barotrauma.Items.Components
{
if (!MathUtils.IsValid(flowPercentage)) { return; }
flowPercentage = MathHelper.Clamp(value, -100.0f, 100.0f);
flowPercentage = MathUtils.Round(flowPercentage, 1.0f);
flowPercentage = MathF.Round(flowPercentage);
}
}
[Editable, Serialize(80.0f, false, description: "How fast the item pumps water in/out when operating at 100%.", alwaysUseInstanceValues: true)]
[Editable, Serialize(80.0f, IsPropertySaveable.No, description: "How fast the item pumps water in/out when operating at 100%.", alwaysUseInstanceValues: true)]
public float MaxFlow
{
get { return maxFlow; }
set { maxFlow = value; }
}
[Editable, Serialize(true, true, alwaysUseInstanceValues: true)]
[Editable, Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public bool IsOn
{
get { return IsActive; }
@@ -83,13 +83,13 @@ namespace Barotrauma.Items.Components
public override bool UpdateWhenInactive => true;
public Pump(Item item, XElement element)
public Pump(Item item, ContentXElement element)
: base(item, element)
{
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Update(float deltaTime, Camera cam)
{
@@ -120,10 +120,6 @@ namespace Barotrauma.Items.Components
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
}
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
if (!HasPower) { return; }
UpdateProjSpecific(deltaTime);
@@ -147,10 +143,9 @@ namespace Barotrauma.Items.Components
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; }
Voltage -= deltaTime;
}
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)
public void InfectBallast(Identifier identifier, bool allowMultiplePerShip = false)
{
Hull hull = item.CurrentHull;
if (hull == null) { return; }
@@ -158,7 +153,7 @@ namespace Barotrauma.Items.Components
if (!allowMultiplePerShip)
{
// if the ship is already infected then do nothing
if (Hull.hullList.Where(h => h.Submarine == hull.Submarine).Any(h => h.BallastFlora != null)) { return; }
if (Hull.HullList.Where(h => h.Submarine == hull.Submarine).Any(h => h.BallastFlora != null)) { return; }
}
if (hull.BallastFlora != null) { return; }
@@ -178,6 +173,24 @@ namespace Barotrauma.Items.Components
#endif
}
/// <summary>
/// Power consumption of the Pump. Only consume power when active and adjust consumption based on condition.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
//There shouldn't be other power connections to this
if (connection != this.powerIn)
{
return 0;
}
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
return currPowerConsumption;
}
partial void UpdateProjSpecific(float deltaTime);
public override void ReceiveSignal(Signal signal, Connection connection)
@@ -218,7 +231,8 @@ namespace Barotrauma.Items.Components
#if CLIENT
if (GameMain.Client != null) { return false; }
#endif
switch (objective.Option.ToLowerInvariant())
switch (objective.Option.Value.ToLowerInvariant())
{
case "pumpout":
#if SERVER
@@ -36,8 +36,8 @@ namespace Barotrauma.Items.Components
private float fireTimer, fireDelay;
private float maxPowerOutput;
private readonly Queue<float> loadQueue = new Queue<float>();
private float minUpdatePowerOut;
private float maxUpdatePowerOut;
private bool unsentChanges;
private float sendUpdateTimer;
@@ -50,7 +50,7 @@ namespace Barotrauma.Items.Components
private bool _powerOn;
[Serialize(defaultValue: false, isSaveable: true)]
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
public bool PowerOn
{
get { return _powerOn; }
@@ -63,9 +63,11 @@ namespace Barotrauma.Items.Components
}
}
protected override PowerPriority Priority { get { return PowerPriority.Reactor; } }
public Character LastAIUser { get; private set; }
[Serialize(defaultValue: false, isSaveable: true)]
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
public bool LastUserWasPlayer { get; private set; }
private Character lastUser;
@@ -81,7 +83,7 @@ namespace Barotrauma.Items.Components
}
}
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, true, description: "How much power (kW) the reactor generates when operating at full capacity.", alwaysUseInstanceValues: true)]
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, IsPropertySaveable.Yes, description: "How much power (kW) the reactor generates when operating at full capacity.", alwaysUseInstanceValues: true)]
public float MaxPowerOutput
{
get { return maxPowerOutput; }
@@ -91,21 +93,21 @@ namespace Barotrauma.Items.Components
}
}
[Editable(0.0f, float.MaxValue), Serialize(120.0f, true, description: "How long the temperature has to stay critical until a meltdown occurs.")]
[Editable(0.0f, float.MaxValue), Serialize(120.0f, IsPropertySaveable.Yes, description: "How long the temperature has to stay critical until a meltdown occurs.")]
public float MeltdownDelay
{
get { return meltDownDelay; }
set { meltDownDelay = Math.Max(value, 0.0f); }
}
[Editable(0.0f, float.MaxValue), Serialize(30.0f, true, description: "How long the temperature has to stay critical until the reactor catches fire.")]
[Editable(0.0f, float.MaxValue), Serialize(30.0f, IsPropertySaveable.Yes, description: "How long the temperature has to stay critical until the reactor catches fire.")]
public float FireDelay
{
get { return fireDelay; }
set { fireDelay = Math.Max(value, 0.0f); }
}
[Serialize(0.0f, true, description: "Current temperature of the reactor (0% - 100%). Indended to be used by StatusEffect conditionals.")]
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current temperature of the reactor (0% - 100%). Indended to be used by StatusEffect conditionals.")]
public float Temperature
{
get { return temperature; }
@@ -116,7 +118,7 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(0.0f, true, description: "Current fission rate of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current fission rate of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
public float FissionRate
{
get { return fissionRate; }
@@ -127,7 +129,7 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(0.0f, true, description: "Current turbine output of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current turbine output of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
public float TurbineOutput
{
get { return turbineOutput; }
@@ -138,7 +140,7 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(0.2f, true, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f, decimals: 3)]
[Serialize(0.2f, IsPropertySaveable.Yes, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f, decimals: 3)]
public float FuelConsumptionRate
{
get { return fuelConsumptionRate; }
@@ -149,14 +151,14 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(false, true, description: "Is the temperature currently critical. Intended to be used by StatusEffect conditionals (setting the value from XML has no effect).")]
[Serialize(false, IsPropertySaveable.Yes, description: "Is the temperature currently critical. Intended to be used by StatusEffect conditionals (setting the value from XML has no effect).")]
public bool TemperatureCritical
{
get { return temperature > allowedTemperature.Y; }
set { /*do nothing*/ }
}
[Serialize(false, true, description: "Is the automatic temperature control currently on. Indended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
[Serialize(false, IsPropertySaveable.Yes, description: "Is the automatic temperature control currently on. Indended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
public bool AutoTemp
{
get { return autoTemp; }
@@ -171,36 +173,36 @@ namespace Barotrauma.Items.Components
private float prevAvailableFuel;
[Serialize(0.0f, true)]
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float AvailableFuel { get; set; }
[Serialize(0.0f, true)]
[Serialize(0.0f, IsPropertySaveable.Yes)]
public new float Load { get; private set; }
[Serialize(0.0f, true)]
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float TargetFissionRate { get; set; }
[Serialize(0.0f, true)]
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float TargetTurbineOutput { get; set; }
[Serialize(0.0f, true)]
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float CorrectTurbineOutput { get; set; }
[Editable, Serialize(true, true)]
[Editable, Serialize(true, IsPropertySaveable.Yes)]
public bool ExplosionDamagesOtherSubs
{
get;
set;
}
public Reactor(Item item, XElement element)
public Reactor(Item item, ContentXElement element)
: base(item, element)
{
IsActive = true;
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Update(float deltaTime, Camera cam)
{
@@ -248,7 +250,7 @@ namespace Barotrauma.Items.Components
//so the player doesn't have to keep adjusting the rate impossibly fast when the load fluctuates heavily
if (!MathUtils.NearlyEqual(MaxPowerOutput, 0.0f))
{
CorrectTurbineOutput += MathHelper.Clamp((Load / MaxPowerOutput * 100.0f) - CorrectTurbineOutput, -10.0f, 10.0f) * deltaTime;
CorrectTurbineOutput += MathHelper.Clamp((Load / MaxPowerOutput * 100.0f) - CorrectTurbineOutput, -20.0f, 20.0f) * deltaTime;
}
//calculate tolerances of the meters based on the skills of the user
@@ -277,25 +279,6 @@ namespace Barotrauma.Items.Components
TurbineOutput = MathHelper.Lerp(turbineOutput, TargetTurbineOutput, deltaTime);
float temperatureFactor = Math.Min(temperature / 50.0f, 1.0f);
currPowerConsumption = -MaxPowerOutput * Math.Min(turbineOutput / 100.0f, temperatureFactor);
//if the turbine output and coolant flow are the optimal range,
//make the generated power slightly adjust according to the load
// (-> the reactor can automatically handle small changes in load as long as the values are roughly correct)
if (turbineOutput > optimalTurbineOutput.X && turbineOutput < optimalTurbineOutput.Y &&
temperature > optimalTemperature.X && temperature < optimalTemperature.Y)
{
float maxAutoAdjust = maxPowerOutput * 0.1f;
autoAdjustAmount = MathHelper.Lerp(
autoAdjustAmount,
MathHelper.Clamp(-Load - currPowerConsumption, -maxAutoAdjust, maxAutoAdjust),
deltaTime * 10.0f);
}
else
{
autoAdjustAmount = MathHelper.Lerp(autoAdjustAmount, 0.0f, deltaTime * 10.0f);
}
currPowerConsumption += autoAdjustAmount;
if (!PowerOn)
{
@@ -304,37 +287,9 @@ namespace Barotrauma.Items.Components
}
else if (autoTemp)
{
UpdateAutoTemp(2.0f, deltaTime);
}
float currentLoad = 0.0f;
List<Connection> connections = item.Connections;
if (connections != null && connections.Count > 0)
{
foreach (Connection connection in connections)
{
if (!connection.IsPower) { continue; }
foreach (Connection recipient in connection.Recipients)
{
if (!(recipient.Item is Item it)) { continue; }
PowerTransfer pt = it.GetComponent<PowerTransfer>();
if (pt == null) { continue; }
//calculate how much external power there is in the grid
//(power coming from somewhere else than this reactor, e.g. batteries)
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0) * 0.95f;
//reduce the external power from the load to prevent overloading the grid
currentLoad = Math.Max(currentLoad, pt.PowerLoad - externalPower);
}
}
UpdateAutoTemp(10.0f, deltaTime * 2f);
}
loadQueue.Enqueue(currentLoad);
while (loadQueue.Count() > 60.0f)
{
Load = loadQueue.Average();
loadQueue.Dequeue();
}
float fuelLeft = 0.0f;
var containedItems = item.OwnInventory?.AllItems;
@@ -405,6 +360,77 @@ namespace Barotrauma.Items.Components
}
}
/// <summary>
/// Returns a negative value (indicating the reactor generates power) when querying the power output connection.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
return connection != null && connection.IsPower && connection.IsOutput ? -1 : 0;
}
/// <summary>
/// Min and Max power output of the reactor based on tolerance
/// </summary>
public override PowerRange MinMaxPowerOut(Connection conn, float load)
{
float tolerance = 1f;
//If within the optimal output allow for slight output adjustments
if (turbineOutput > optimalTurbineOutput.X && turbineOutput < optimalTurbineOutput.Y &&
temperature > optimalTemperature.X && temperature < optimalTemperature.Y)
{
tolerance = 3f;
}
float temperatureFactor = Math.Min(temperature / 50.0f, 1.0f);
float minOutput = MaxPowerOutput * Math.Clamp(Math.Min((turbineOutput - tolerance) / 100.0f, temperatureFactor), 0, 1);
float maxOutput = MaxPowerOutput * Math.Min((turbineOutput + tolerance) / 100.0f, temperatureFactor);
minUpdatePowerOut = minOutput;
maxUpdatePowerOut = maxOutput;
float reactorMax = PowerOn ? MaxPowerOutput : maxUpdatePowerOut;
return new PowerRange(minOutput, maxOutput, reactorMax);
}
/// <summary>
/// Determine how much power to output based on the load. The load is divided between reactors according to their maximum output in multi-reactor setups.
/// </summary>
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
{
//Load must be calculated at this stage instead of at gridResolved to remove influence of lower priority devices
float loadLeft = MathHelper.Max(load - power,0);
float expectedPower = MathHelper.Clamp(loadLeft, minMaxPower.Min, minMaxPower.Max);
//Delta ratio of Min and Max power output capability of the grid
float ratio = MathHelper.Max((loadLeft - minMaxPower.Min) / (minMaxPower.Max - minMaxPower.Min), 0);
if (float.IsInfinity(ratio))
{
ratio = 0;
}
float output = MathHelper.Clamp(ratio * (maxUpdatePowerOut - minUpdatePowerOut) + minUpdatePowerOut, minUpdatePowerOut, maxUpdatePowerOut);
float newLoad = loadLeft;
//Adjust behaviour for multi reactor setup
if (MaxPowerOutput != minMaxPower.ReactorMaxOutput)
{
float idealLoad = MaxPowerOutput / minMaxPower.ReactorMaxOutput * loadLeft;
float loadAdjust = MathHelper.Clamp((ratio - 0.5f) * 25 + idealLoad - (turbineOutput / 100 * MaxPowerOutput), -MaxPowerOutput / 100, MaxPowerOutput / 100);
newLoad = MathHelper.Clamp(loadLeft - (expectedPower + output) + loadAdjust, 0, loadLeft);
}
if (float.IsNegative(newLoad))
{
newLoad = 0.0f;
}
Load = newLoad;
currPowerConsumption = -output;
return output;
}
private float GetGeneratedHeat(float fissionRate)
{
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
@@ -595,7 +621,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
character.AIController.SteeringManager.Reset();
bool shutDown = objective.Option.Equals("shutdown", StringComparison.OrdinalIgnoreCase);
bool shutDown = objective.Option == "shutdown";
IsActive = true;
@@ -624,7 +650,7 @@ namespace Barotrauma.Items.Components
var containObjective = AIContainItems<Reactor>(container, character, objective, itemCount: 1, equip: true, removeEmpty: true, spawnItemIfNotFound: !character.IsOnPlayerTeam, dropItemOnDeselected: true);
containObjective.Completed += ReportFuelRodCount;
containObjective.Abandoned += ReportFuelRodCount;
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
character.Speak(TextManager.Get("DialogReactorFuel").Value, null, 0.0f, "reactorfuel".ToIdentifier(), 30.0f);
void ReportFuelRodCount()
{
@@ -633,12 +659,12 @@ namespace Barotrauma.Items.Components
int remainingFuelRods = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("reactorfuel") && i.Condition > 1);
if (remainingFuelRods == 0)
{
character.Speak(TextManager.Get("DialogOutOfFuelRods"), null, 0.0f, "outoffuelrods", 30.0f);
character.Speak(TextManager.Get("DialogOutOfFuelRods").Value, null, 0.0f, "outoffuelrods".ToIdentifier(), 30.0f);
outOfFuel = true;
}
else if (remainingFuelRods < 3)
{
character.Speak(TextManager.Get("DialogLowOnFuelRods"), null, 0.0f, "lowonfuelrods", 30.0f);
character.Speak(TextManager.Get("DialogLowOnFuelRods").Value, null, 0.0f, "lowonfuelrods".ToIdentifier(), 30.0f);
}
}
}
@@ -655,7 +681,7 @@ namespace Barotrauma.Items.Components
}
else
{
character.Speak(TextManager.Get("DialogReactorIsBroken"), identifier: "reactorisbroken", minDurationBetweenSimilar: 30.0f);
character.Speak(TextManager.Get("DialogReactorIsBroken").Value, identifier: "reactorisbroken".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
}
}
if (TooMuchFuel())
@@ -676,7 +702,7 @@ namespace Barotrauma.Items.Components
{
if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
{
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
character.Speak(TextManager.Get("DialogReactorTaken").Value, null, 0.0f, "reactortaken".ToIdentifier(), 10.0f);
}
}
}
@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
get { return connectedTransducers.Select(t => t.Transducer); }
}
[Serialize(DefaultSonarRange, false, description: "The maximum range of the sonar.")]
[Serialize(DefaultSonarRange, IsPropertySaveable.No, description: "The maximum range of the sonar.")]
public float Range
{
get { return range; }
@@ -92,29 +92,29 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(false, false, description: "Should the sonar display the walls of the submarine it is inside.")]
[Serialize(false, IsPropertySaveable.No, description: "Should the sonar display the walls of the submarine it is inside.")]
public bool DetectSubmarineWalls
{
get;
set;
}
[Editable, Serialize(false, false, description: "Does the sonar have to be connected to external transducers to work.")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have to be connected to external transducers to work.")]
public bool UseTransducers
{
get;
set;
}
[Editable, Serialize(false, false, description: "Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
public bool CenterOnTransducers
{
get;
set;
}
[Editable, Serialize(false, false, description: "Does the sonar have mineral scanning mode. " +
"Only available in-game when the Item has no Steering component.")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. " +
"Only available in-game when the Item has no Steering component.")]
public bool HasMineralScanner { get; set; }
public float Zoom
@@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components
public override bool RecreateGUIOnResolutionChange => true;
public Sonar(Item item, XElement element)
public Sonar(Item item, ContentXElement element)
: base(item, element)
{
connectedTransducers = new List<ConnectedTransducer>();
@@ -155,12 +155,10 @@ namespace Barotrauma.Items.Components
CurrentMode = Mode.Passive;
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Update(float deltaTime, Camera cam)
{
currPowerConsumption = (currentMode == Mode.Active) ? powerConsumption : powerConsumption * PassivePowerConsumption;
UpdateOnActiveEffects(deltaTime);
if (UseTransducers)
@@ -240,8 +238,19 @@ namespace Barotrauma.Items.Components
++pingIndex;
}
}
}
Voltage -= deltaTime;
/// <summary>
/// Power consumption of the sonar. Only consume power when active and adjust the consumption based on the sonar mode.
/// </summary>
public override float GetCurrentPowerConsumption(Connection connection = null)
{
if (connection != powerIn || !IsActive)
{
return 0;
}
return (currentMode == Mode.Active) ? powerConsumption : powerConsumption * PassivePowerConsumption;
}
public override bool Use(float deltaTime, Character character = null)
@@ -266,7 +275,8 @@ namespace Barotrauma.Items.Components
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; }
if (Vector2.DistanceSquared(c.WorldPosition, item.WorldPosition) > range * range) { continue; }
string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition);
#warning This is not the best key for a dictionary.
string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition).Value;
if (!targetGroups.ContainsKey(directionName))
{
targetGroups.Add(directionName, new List<Character>());
@@ -289,9 +299,10 @@ namespace Barotrauma.Items.Components
if (character.IsOnPlayerTeam)
{
character.Speak(TextManager.GetWithVariables(dialogTag, new string[2] { "[direction]", "[count]" },
new string[2] { targetGroup.Key.ToString(), targetGroup.Value.Count.ToString() },
new bool[2] { true, false }), null, 0, "sonartarget" + targetGroup.Value[0].ID, 60);
character.Speak(TextManager.GetWithVariables(dialogTag,
("[direction]", targetGroup.Key.ToString(), FormatCapitals.Yes),
("[count]", targetGroup.Value.Count.ToString(), FormatCapitals.No)).Value,
null, 0, $"sonartarget{targetGroup.Value[0].ID}".ToIdentifier(), 60);
}
//prevent the character from reporting other targets in the group
@@ -304,7 +315,7 @@ namespace Barotrauma.Items.Components
return true;
}
private string GetDirectionName(Vector2 dir)
private LocalizedString GetDirectionName(Vector2 dir)
{
float angle = MathUtils.WrapAngleTwoPi((float)-Math.Atan2(dir.Y, dir.X) + MathHelper.PiOver2);
@@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components
public Sonar ConnectedSonar;
public SonarTransducer(Item item, XElement element) : base(item, element)
public SonarTransducer(Item item, ContentXElement element) : base(item, element)
{
IsActive = true;
}
@@ -19,8 +19,6 @@ namespace Barotrauma.Items.Components
{
UpdateOnActiveEffects(deltaTime);
CurrPowerConsumption = powerConsumption * (ConnectedSonar?.CurrentMode == Sonar.Mode.Active ? 1.0f : Sonar.PassivePowerConsumption);
if (Voltage >= MinVoltage)
{
sendSignalTimer += deltaTime;
@@ -31,5 +29,15 @@ namespace Barotrauma.Items.Components
}
}
}
public override float GetCurrentPowerConsumption(Connection connection = null)
{
if (connection != powerIn || !IsActive)
{
return 0;
}
return PowerConsumption * (ConnectedSonar?.CurrentMode == Sonar.Mode.Active ? 1.0f : Sonar.PassivePowerConsumption);
}
}
}
@@ -1,4 +1,4 @@
using Barotrauma.Networking;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
@@ -106,7 +106,7 @@ namespace Barotrauma.Items.Components
}
[Editable(0.0f, 1.0f, decimals: 4),
Serialize(0.5f, true, description: "How full the ballast tanks should be when the submarine is not being steered upwards/downwards."
Serialize(0.5f, IsPropertySaveable.Yes, description: "How full the ballast tanks should be when the submarine is not being steered upwards/downwards."
+ " Can be used to compensate if the ballast tanks are too large/small relative to the size of the submarine.")]
public float NeutralBallastLevel
{
@@ -117,7 +117,7 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(1000.0f, true, description: "How close the docking port has to be to another docking port for the docking mode to become active.")]
[Serialize(1000.0f, IsPropertySaveable.Yes, description: "How close the docking port has to be to another docking port for the docking mode to become active.")]
public float DockingAssistThreshold
{
get;
@@ -231,14 +231,14 @@ namespace Barotrauma.Items.Components
}
#endregion
public Steering(Item item, XElement element)
public Steering(Item item, ContentXElement element)
: base(item, element)
{
IsActive = true;
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void OnItemLoaded()
{
@@ -292,8 +292,6 @@ namespace Barotrauma.Items.Components
controlledSub = sonar.ConnectedTransducers.Any() ? sonar.ConnectedTransducers.First().Item.Submarine : null;
}
currPowerConsumption = powerConsumption;
if (Voltage < MinVoltage) { return; }
if (user != null && user.Removed)
@@ -405,7 +403,7 @@ namespace Barotrauma.Items.Components
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
user.Info.IncreaseSkillLevel(
"helm",
"helm".ToIdentifier(),
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime);
}
@@ -724,7 +722,7 @@ namespace Barotrauma.Items.Components
{
if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
{
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
character.Speak(TextManager.Get("DialogSteeringTaken").Value, null, 0.0f, "steeringtaken".ToIdentifier(), 10.0f);
}
}
user = character;
@@ -738,7 +736,7 @@ namespace Barotrauma.Items.Components
}
else
{
character.Speak(TextManager.Get("DialogNavTerminalIsBroken"), identifier: "navterminalisbroken", minDurationBetweenSimilar: 30.0f);
character.Speak(TextManager.Get("DialogNavTerminalIsBroken").Value, identifier: "navterminalisbroken".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
}
}
@@ -748,64 +746,72 @@ namespace Barotrauma.Items.Components
AutoPilot = true;
}
IncreaseSkillLevel(user, deltaTime);
switch (objective.Option.ToLowerInvariant())
if (objective.Option == "maintainposition")
{
case "maintainposition":
if (objective.Override)
{
SetMaintainPosition();
}
break;
case "navigateback":
if (Level.IsLoadedOutpost) { break; }
if (objective.Override)
{
SetMaintainPosition();
}
}
else if (!Level.IsLoadedOutpost)
{
if (objective.Option == "navigateback")
{
if (DockingSources.Any(d => d.Docked))
{
item.SendSignal("1", "toggle_docking");
}
if (objective.Override)
{
if (MaintainPos || LevelEndSelected || !LevelStartSelected || navigateTactically)
{
unsentChanges = true;
}
SetDestinationLevelStart();
}
break;
case "navigatetodestination":
if (Level.IsLoadedOutpost) { break; }
}
else if (objective.Option == "navigatetodestination")
{
if (DockingSources.Any(d => d.Docked))
{
item.SendSignal("1", "toggle_docking");
}
if (objective.Override)
{
if (MaintainPos || !LevelEndSelected || LevelStartSelected || navigateTactically)
{
unsentChanges = true;
}
SetDestinationLevelEnd();
}
break;
case "navigatetactical":
if (Level.IsLoadedOutpost) { break; }
}
else if (objective.Option == "navigatetactical")
{
if (DockingSources.Any(d => d.Docked))
{
item.SendSignal("1", "toggle_docking");
}
if (objective.Override)
{
if (MaintainPos || LevelEndSelected || LevelStartSelected || !navigateTactically)
{
unsentChanges = true;
}
SetDestinationTactical();
}
break;
}
}
sonar?.AIOperate(deltaTime, character, objective);
if (!MaintainPos && showIceSpireWarning && character.IsOnPlayerTeam)
{
character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
character.Speak(TextManager.Get("dialogicespirespottedsonar").Value, null, 0.0f, "icespirespottedsonar".ToIdentifier(), 60.0f);
}
return false;
}
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
set { oxygenFlow = Math.Max(value, 0.0f); }
}
public Vent (Item item, XElement element) : base(item, element) { }
public Vent (Item item, ContentXElement element) : base(item, element) { }
public override void Update(float deltaTime, Camera cam)
{