v1.4.4.1 (Blood in the Water Update)

This commit is contained in:
Regalis11
2024-04-24 18:09:05 +03:00
parent 89b91d1c3e
commit ff1b8951a7
397 changed files with 15250 additions and 6479 deletions
@@ -34,10 +34,16 @@ namespace Barotrauma.Items.Components
get { return outputContainer; }
}
/// <summary>
/// Should the output items left in the deconstructor be automatically moved to the main sub at the end of the round
/// if the deconstructor is not in the main sub?
/// </summary>
public bool RelocateOutputToMainSub;
[Serialize(false, IsPropertySaveable.Yes)]
public bool DeconstructItemsSimultaneously { get; set; }
[Editable, Serialize(1.0f, IsPropertySaveable.Yes)]
[Editable(MinValueFloat = 0.1f, MaxValueFloat = 1000), Serialize(1.0f, IsPropertySaveable.Yes)]
public float DeconstructionSpeed { get; set; }
public Deconstructor(Item item, ContentXElement element)
@@ -290,6 +296,10 @@ namespace Barotrauma.Items.Components
spawnedItem.AllowStealing = targetItem.AllowStealing;
spawnedItem.OriginalOutpost = targetItem.OriginalOutpost;
spawnedItem.SpawnedInCurrentOutpost = targetItem.SpawnedInCurrentOutpost;
if (RelocateOutputToMainSub && user is { AIController: HumanAIController humanAi })
{
humanAi.HandleRelocation(spawnedItem);
}
for (int i = 0; i < outputContainer.Capacity; i++)
{
var containedItem = outputContainer.Inventory.GetItemAt(i);
@@ -318,7 +328,12 @@ namespace Barotrauma.Items.Components
}
}
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + targetItem.Prefab.Identifier);
if (targetItem.Prefab.ContentPackage == ContentPackageManager.VanillaCorePackage &&
/* we don't need info of every item, we can get a good sample size just by logging 5% */
Rand.Range(0.0f, 1.0f) < 0.05f)
{
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + targetItem.Prefab.Identifier);
}
if (targetItem.AllowDeconstruct && allowRemove)
{
@@ -329,6 +344,10 @@ namespace Barotrauma.Items.Components
foreach (Item outputItem in ic.Inventory.AllItemsMod)
{
tryPutInOutputSlots(outputItem);
if (RelocateOutputToMainSub && user != null && user.AIController is HumanAIController humanAi)
{
humanAi.HandleRelocation(outputItem);
}
}
}
inputContainer.Inventory.RemoveItem(targetItem);
@@ -436,11 +455,12 @@ namespace Barotrauma.Items.Components
}
}
private void SetActive(bool active, Character user = null)
public void SetActive(bool active, Character user = null, bool createNetworkEvent = false)
{
PutItemsToLinkedContainer();
this.user = user;
RelocateOutputToMainSub = false;
if (inputContainer.Inventory.IsEmpty()) { active = false; }
@@ -453,6 +473,10 @@ namespace Barotrauma.Items.Components
{
GameServer.Log(GameServer.CharacterLogName(user) + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
}
if (createNetworkEvent)
{
item.CreateServerEvent(this);
}
#endif
if (!IsActive)
{
@@ -462,7 +486,11 @@ namespace Barotrauma.Items.Components
#if CLIENT
else
{
HintManager.OnStartDeconstructing(user, this);
HintManager.OnStartDeconstructing(user, this);
if (Item.Submarine is { Info.IsOutpost: true } && user is { IsBot: true })
{
HintManager.OnItemMarkedForRelocation();
}
}
#endif
@@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components
private ItemContainer inputContainer, outputContainer;
[Serialize(1.0f, IsPropertySaveable.Yes)]
[Editable(MinValueFloat = 0.1f, MaxValueFloat = 1000), Serialize(1.0f, IsPropertySaveable.Yes)]
public float FabricationSpeed { get; set; }
[Serialize(1.0f, IsPropertySaveable.Yes)]
@@ -469,7 +469,10 @@ namespace Barotrauma.Items.Components
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
quality = GetFabricatedItemQuality(fabricatedItem, user).RollQuality();
quality =
fabricatedItem.TargetItem.MaxStackSize > 1 ?
GetFabricatedItemQuality(fabricatedItem, user).Quality :
GetFabricatedItemQuality(fabricatedItem, user).RollQuality();
}
int amount = (int)fabricationitemAmount.Value;
@@ -490,7 +493,12 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < amount; i++)
{
float outCondition = fabricatedItem.OutCondition;
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
if (fabricatedItem.TargetItem.ContentPackage == ContentPackageManager.VanillaCorePackage &&
/* we don't need info of every fabricated item, we can get a good sample size just by logging 5% */
Rand.Range(0.0f, 1.0f) < 0.05f)
{
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
}
if (i < amountFittingContainer)
{
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
@@ -577,11 +585,11 @@ namespace Barotrauma.Items.Components
public static float CalculateBonusRollPercentage(float skillLevel, float target)
=> Math.Clamp((skillLevel - target) / (100f - target) * 100f, min: 0, max: 100);
public readonly record struct QualityResult(int Quality, float PlusOnePercentage, float PlusTwoPercentage)
public readonly record struct QualityResult(int Quality, bool HasRandomQuality, float PlusOnePercentage, float PlusTwoPercentage)
{
public static readonly QualityResult Empty = new QualityResult(0, 0, 0);
public static readonly QualityResult Empty = new QualityResult(0, true, 0, 0);
public bool HasRandomQualityRollChance => PlusOnePercentage > 0f || PlusTwoPercentage > 0f;
public bool HasRandomQualityRollChance => HasRandomQuality && (PlusOnePercentage > 0f || PlusTwoPercentage > 0f);
// The total real world percentage for a roll to succeed, taking into account that +1 needs to succeed for +2 to be attempted and
// that the chance for only +1 goes down as +2 increases since some of the +1's will turn into +2s
@@ -676,9 +684,23 @@ namespace Barotrauma.Items.Components
}
}
bool hasRandomQuality = !(fabricatedItem.TargetItem.MaxStackSize > 1); //don't randomise items with a stacksize > 1
float PlusOnePercentage = plusOne.Match(some: static f => f, none: static () => 0f);
float PlusTwoPercentage = plusTwo.Match(some: static f => f, none: static () => 0f);
if (!hasRandomQuality && PlusOnePercentage > 0)
{
quality++;
if (PlusTwoPercentage > 0)
{
quality++;
}
}
return new QualityResult(quality,
PlusOnePercentage: plusOne.Match(some: static f => f, none: static () => 0f),
PlusTwoPercentage: plusTwo.Match(some: static f => f, none: static () => 0f));
hasRandomQuality,
PlusOnePercentage,
PlusTwoPercentage);
}
partial void UpdateRequiredTimeProjSpecific();
@@ -690,6 +712,8 @@ namespace Barotrauma.Items.Components
GameSession.GetSessionCrewCharacters(CharacterType.Bot).Any(c => c.HasRecipeForItem(item.Identifier));
}
private readonly HashSet<Item> usedIngredients = new HashSet<Item>();
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
{
if (fabricableItem == null) { return false; }
@@ -733,22 +757,26 @@ namespace Barotrauma.Items.Components
return false;
}
//maintain a list of used ingredients so we don't end up considering the same item a suitable for multiple required ingredients
usedIngredients.Clear();
return fabricableItem.RequiredItems.All(requiredItem =>
{
int availablePrefabsAmount = 0;
int availableItemsAmount = 0;
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
{
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
if (!availableIngredients.TryGetValue(requiredPrefab.Identifier, out var availableItems)) { continue; }
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
foreach (Item availablePrefab in availablePrefabs)
foreach (Item availableItem in availableItems)
{
if (requiredItem.IsConditionSuitable(availablePrefab.ConditionPercentage))
if (usedIngredients.Contains(availableItem)) { continue; }
if (requiredItem.IsConditionSuitable(availableItem.ConditionPercentage))
{
availablePrefabsAmount++;
usedIngredients.Add(availableItem);
availableItemsAmount++;
}
if (availablePrefabsAmount >= requiredItem.Amount)
if (availableItemsAmount >= requiredItem.Amount)
{
return true;
}
@@ -91,7 +91,7 @@ namespace Barotrauma.Items.Components
ventList.Clear();
foreach (MapEntity entity in item.linkedTo)
{
if (!(entity is Item linkedItem)) { continue; }
if (entity is not Item linkedItem) { continue; }
Vent vent = linkedItem.GetComponent<Vent>();
if (vent?.Item.CurrentHull == null) { continue; }
@@ -132,5 +132,19 @@ namespace Barotrauma.Items.Components
vent.IsActive = true;
}
}
public float GetVentOxygenFlow(Vent targetVent)
{
if (ventList == null)
{
GetVents();
}
foreach ((Vent vent, float hullVolume) in ventList)
{
if (vent != targetVent) { continue; }
return generatedAmount * 100.0f * (hullVolume / totalHullVolume);
}
return 0.0f;
}
}
}
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -215,10 +214,10 @@ namespace Barotrauma.Items.Components
activePings[currentPingIndex].Direction = pingDirection;
activePings[currentPingIndex].State = 0.0f;
activePings[currentPingIndex].PrevPingRadius = 0.0f;
if (item.AiTarget != null)
foreach (AITarget aiTarget in GetAITargets())
{
item.AiTarget.SectorDegrees = useDirectionalPing ? DirectionalPingSector : 360.0f;
item.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
aiTarget.SectorDegrees = useDirectionalPing ? DirectionalPingSector : 360.0f;
aiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
}
item.Use(deltaTime);
}
@@ -231,10 +230,10 @@ namespace Barotrauma.Items.Components
for (var pingIndex = 0; pingIndex < activePingsCount;)
{
if (item.AiTarget != null)
foreach (AITarget aiTarget in GetAITargets())
{
float range = MathUtils.InverseLerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, Range * activePings[pingIndex].State / zoom);
item.AiTarget.SoundRange = Math.Max(item.AiTarget.SoundRange, MathHelper.Lerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, range));
float range = MathUtils.InverseLerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, Range * activePings[pingIndex].State / zoom);
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range));
}
if (activePings[pingIndex].State > 1.0f)
{
@@ -254,6 +253,24 @@ namespace Barotrauma.Items.Components
}
}
private IEnumerable<AITarget> GetAITargets()
{
if (!UseTransducers)
{
if (item.AiTarget != null) { yield return item.AiTarget; }
}
else
{
foreach (var transducer in connectedTransducers)
{
if (transducer.Transducer.Item.AiTarget != null)
{
yield return transducer.Transducer.Item.AiTarget;
}
}
}
}
/// <summary>
/// Power consumption of the sonar. Only consume power when active and adjust the consumption based on the sonar mode.
/// </summary>
@@ -22,6 +22,11 @@ namespace Barotrauma.Items.Components
private const float AutoPilotMaxSpeed = 0.5f;
private const float AIPilotMaxSpeed = 1.0f;
/// <summary>
/// How many units before crush depth the pressure warning is shown
/// </summary>
public const float PressureWarningThreshold = 500.0f;
/// <summary>
/// How fast the steering vector adjusts when the nav terminal is operated by something else than a character (= signals)
/// </summary>