Unstable v0.1300.0.0 (February 19th 2021)
This commit is contained in:
@@ -90,6 +90,13 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(UseEnvironment.Both, false, 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.")]
|
||||
public bool DrawUserBehind
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ControlCharacterPose
|
||||
{
|
||||
get { return limbPositions.Count > 0; }
|
||||
@@ -236,12 +243,12 @@ namespace Barotrauma.Items.Components
|
||||
case LimbType.RightHand:
|
||||
case LimbType.RightForearm:
|
||||
case LimbType.RightArm:
|
||||
if (user.SelectedItems[0] != null) { continue; }
|
||||
if (user.Inventory.GetItemInLimbSlot(InvSlotType.RightHand) != null) { continue; }
|
||||
break;
|
||||
case LimbType.LeftHand:
|
||||
case LimbType.LeftForearm:
|
||||
case LimbType.LeftArm:
|
||||
if ( user.SelectedItems[1] != null) { continue; }
|
||||
if (user.Inventory.GetItemInLimbSlot(InvSlotType.LeftHand) != null) { continue; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -388,6 +395,12 @@ namespace Barotrauma.Items.Components
|
||||
limb.PullJointEnabled = false;
|
||||
}
|
||||
|
||||
//disable flipping for 0.5 seconds, because flipping the character when it's in a weird pose (e.g. lying in bed) can mess up the ragdoll
|
||||
if (character.AnimController is HumanoidAnimController humanoidAnim)
|
||||
{
|
||||
humanoidAnim.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction == this.item) { character.SelectedConstruction = null; }
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
@@ -470,6 +483,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasAccess(Character character)
|
||||
{
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
return base.HasAccess(character);
|
||||
}
|
||||
|
||||
partial void HideHUDs(bool value);
|
||||
}
|
||||
}
|
||||
|
||||
+83
-57
@@ -1,5 +1,7 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -59,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
MoveInputQueue();
|
||||
|
||||
if (inputContainer == null || inputContainer.Inventory.Items.All(i => i == null))
|
||||
if (inputContainer == null || inputContainer.Inventory.IsEmpty())
|
||||
{
|
||||
SetActive(false);
|
||||
return;
|
||||
@@ -79,7 +81,7 @@ namespace Barotrauma.Items.Components
|
||||
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
|
||||
progressTimer += deltaTime * Math.Min(Voltage, 1.0f);
|
||||
|
||||
var targetItem = inputContainer.Inventory.Items.LastOrDefault(i => i != null);
|
||||
var targetItem = inputContainer.Inventory.LastOrDefault();
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / DeconstructionSpeed : 1.0f;
|
||||
@@ -87,78 +89,107 @@ namespace Barotrauma.Items.Components
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
if (progressTimer > deconstructTime)
|
||||
{
|
||||
int emptySlots = outputContainer.Inventory.Items.Where(i => i == null).Count();
|
||||
// In multiplayer, the server handles the deconstruction into new items
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
if (targetItem.Prefab.RandomDeconstructionOutput)
|
||||
{
|
||||
int amount = targetItem.Prefab.RandomDeconstructionOutputAmount;
|
||||
List<int> deconstructItemIndexes = new List<int>();
|
||||
for (int i = 0; i < targetItem.Prefab.DeconstructItems.Count; i++)
|
||||
{
|
||||
deconstructItemIndexes.Add(i);
|
||||
}
|
||||
List<float> commonness = targetItem.Prefab.DeconstructItems.Select(i => i.Commonness).ToList();
|
||||
List<DeconstructItem> products = new List<DeconstructItem>();
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
if (deconstructItemIndexes.Count < 1) { break; }
|
||||
var itemIndex = ToolBox.SelectWeightedRandom(deconstructItemIndexes, commonness, Rand.RandSync.Unsynced);
|
||||
products.Add(targetItem.Prefab.DeconstructItems[itemIndex]);
|
||||
var removeIndex = deconstructItemIndexes.IndexOf(itemIndex);
|
||||
deconstructItemIndexes.RemoveAt(removeIndex);
|
||||
commonness.RemoveAt(removeIndex);
|
||||
}
|
||||
foreach (DeconstructItem deconstructProduct in products)
|
||||
{
|
||||
CreateDeconstructProduct(deconstructProduct);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
{
|
||||
CreateDeconstructProduct(deconstructProduct);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateDeconstructProduct(DeconstructItem deconstructProduct)
|
||||
{
|
||||
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
|
||||
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) continue;
|
||||
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 + "\"!");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
float condition = deconstructProduct.CopyCondition ?
|
||||
percentageHealth * itemPrefab.Health :
|
||||
itemPrefab.Health * deconstructProduct.OutCondition;
|
||||
|
||||
//container full, drop the items outside the deconstructor
|
||||
if (emptySlots <= 0)
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, item.Position, item.Submarine, condition);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition);
|
||||
emptySlots--;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (targetItem.Prefab.AllowDeconstruct)
|
||||
{
|
||||
//drop all items that are inside the deconstructed item
|
||||
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
|
||||
for (int i = 0; i < outputContainer.Capacity; i++)
|
||||
{
|
||||
if (ic?.Inventory?.Items == null) { continue; }
|
||||
foreach (Item containedItem in ic.Inventory.Items)
|
||||
var containedItem = outputContainer.Inventory.GetItemAt(i);
|
||||
if (containedItem?.Combine(spawnedItem, null) ?? false)
|
||||
{
|
||||
containedItem?.Drop(dropper: null, createNetworkEvent: true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
});
|
||||
}
|
||||
|
||||
if (targetItem.Prefab.AllowDeconstruct)
|
||||
{
|
||||
//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();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!outputContainer.Inventory.CanBePut(targetItem))
|
||||
{
|
||||
targetItem.Drop(dropper: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outputContainer.Inventory.Items.All(i => i != null))
|
||||
{
|
||||
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;
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
progressTimer = 0.0f;
|
||||
progressState = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void PutItemsToLinkedContainer()
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (outputContainer.Inventory.Items.All(it => it == null)) return;
|
||||
if (outputContainer.Inventory.IsEmpty()) { return; }
|
||||
|
||||
foreach (MapEntity linkedTo in item.linkedTo)
|
||||
{
|
||||
@@ -168,13 +199,7 @@ namespace Barotrauma.Items.Components
|
||||
if (fabricator != null) { continue; }
|
||||
var itemContainer = linkedItem.GetComponent<ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
|
||||
foreach (Item containedItem in outputContainer.Inventory.Items)
|
||||
{
|
||||
if (containedItem == null) { continue; }
|
||||
if (itemContainer.Inventory.Items.All(it => it != null)) { break; }
|
||||
itemContainer.Inventory.TryPutItem(containedItem, user: null, createNetworkEvent: true);
|
||||
}
|
||||
outputContainer.Inventory.AllItemsMod.ForEach(containedItem => itemContainer.Inventory.TryPutItem(containedItem, user: null, createNetworkEvent: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,9 +211,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
for (int i = inputContainer.Inventory.Capacity - 2; i >= 0; i--)
|
||||
{
|
||||
if (inputContainer.Inventory.Items[i] != null && inputContainer.Inventory.Items[i + 1] == null)
|
||||
while (inputContainer.Inventory.GetItemAt(i) is Item item1 && inputContainer.Inventory.CanBePut(item1, i + 1))
|
||||
{
|
||||
inputContainer.Inventory.TryPutItem(inputContainer.Inventory.Items[i], i + 1, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: true);
|
||||
if (!inputContainer.Inventory.TryPutItem(item1, i + 1, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: true))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,18 +225,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
PutItemsToLinkedContainer();
|
||||
|
||||
if (inputContainer.Inventory.Items.All(i => i == null)) { active = false; }
|
||||
if (inputContainer.Inventory.IsEmpty()) { active = false; }
|
||||
|
||||
IsActive = active;
|
||||
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
|
||||
|
||||
#if SERVER
|
||||
if (user != null)
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!IsActive)
|
||||
{
|
||||
progressTimer = 0.0f;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
|
||||
public Character User;
|
||||
|
||||
[Editable(0.0f, 10000000.0f),
|
||||
Serialize(2000.0f, true, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
|
||||
Serialize(500.0f, true, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
|
||||
public float MaxForce
|
||||
{
|
||||
get { return maxForce; }
|
||||
@@ -46,6 +46,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
public bool DisablePropellerDamage
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public float Force
|
||||
{
|
||||
get { return force;}
|
||||
@@ -117,6 +124,7 @@ namespace Barotrauma.Items.Components
|
||||
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);
|
||||
if (item.Submarine.FlippedX) { currForce *= -1; }
|
||||
item.Submarine.ApplyForce(currForce);
|
||||
UpdatePropellerDamage(deltaTime);
|
||||
float maxChangeSpeed = 0.5f;
|
||||
@@ -130,7 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
if (particleTimer <= 0.0f)
|
||||
{
|
||||
Vector2 particleVel = -currForce.ClampLength(5000.0f) / 5.0f;
|
||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos,
|
||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos * item.Scale,
|
||||
particleVel * Rand.Range(0.9f, 1.1f),
|
||||
0.0f, item.CurrentHull);
|
||||
particleTimer = 1.0f / particlesPerSec;
|
||||
@@ -154,19 +162,22 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void UpdatePropellerDamage(float deltaTime)
|
||||
{
|
||||
if (DisablePropellerDamage) { return; }
|
||||
|
||||
damageTimer += deltaTime;
|
||||
if (damageTimer < 0.5f) return;
|
||||
if (damageTimer < 0.5f) { return; }
|
||||
damageTimer = 0.1f;
|
||||
|
||||
if (propellerDamage == null) return;
|
||||
Vector2 propellerWorldPos = item.WorldPosition + PropellerPos;
|
||||
if (propellerDamage == null) { return; }
|
||||
|
||||
float scaledDamageRange = propellerDamage.DamageRange * item.Scale;
|
||||
|
||||
Vector2 propellerWorldPos = item.WorldPosition + PropellerPos * item.Scale;
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.Submarine != null || !character.Enabled || character.Removed) continue;
|
||||
|
||||
float dist = Vector2.DistanceSquared(character.WorldPosition, propellerWorldPos);
|
||||
if (dist > propellerDamage.DamageRange * propellerDamage.DamageRange) continue;
|
||||
|
||||
if (character.Submarine != null || !character.Enabled || character.Removed) { continue; }
|
||||
float distSqr = Vector2.DistanceSquared(character.WorldPosition, propellerWorldPos);
|
||||
if (distSqr > scaledDamageRange * scaledDamageRange) { continue; }
|
||||
character.LastDamageSource = item;
|
||||
propellerDamage.DoDamage(null, character, propellerWorldPos, 1.0f, true);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -96,6 +97,12 @@ namespace Barotrauma.Items.Components
|
||||
fabricationRecipes.Add(recipe);
|
||||
}
|
||||
}
|
||||
fabricationRecipes.Sort((r1, r2) =>
|
||||
{
|
||||
int hash1 = (int)r1.TargetItem.UIntIdentifier;
|
||||
int hash2 = (int)r2.TargetItem.UIntIdentifier;
|
||||
return hash1 - hash2;
|
||||
});
|
||||
|
||||
state = FabricatorState.Stopped;
|
||||
|
||||
@@ -142,7 +149,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return (picker != null);
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
public void RemoveFabricationRecipes(List<string> allowedIdentifiers)
|
||||
@@ -161,10 +168,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void CreateRecipes();
|
||||
|
||||
private void StartFabricating(FabricationRecipe selectedItem, Character user)
|
||||
private void StartFabricating(FabricationRecipe selectedItem, Character user, bool addToServerLog = true)
|
||||
{
|
||||
if (selectedItem == null) { return; }
|
||||
if (!outputContainer.Inventory.IsEmpty()) { return; }
|
||||
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem)) { return; }
|
||||
|
||||
#if CLIENT
|
||||
itemList.Enabled = false;
|
||||
@@ -190,7 +197,7 @@ namespace Barotrauma.Items.Components
|
||||
State = FabricatorState.Active;
|
||||
}
|
||||
#if SERVER
|
||||
if (user != null)
|
||||
if (user != null && addToServerLog)
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
@@ -298,20 +305,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Character tempUser = user;
|
||||
if (outputContainer.Inventory.Items.All(i => i != null))
|
||||
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem);
|
||||
for (int i = 0; i < fabricatedItem.Amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
}
|
||||
}
|
||||
|
||||
static void onItemSpawned(Item spawnedItem, Character user)
|
||||
{
|
||||
if (user != null && user.TeamID != Character.TeamType.None)
|
||||
if (user != null && user.TeamID != CharacterTeamType.None)
|
||||
{
|
||||
foreach (WifiComponent wifiComponent in spawnedItem.GetComponents<WifiComponent>())
|
||||
{
|
||||
@@ -328,10 +339,23 @@ namespace Barotrauma.Items.Components
|
||||
user.Info.IncreaseSkillLevel(
|
||||
skill.Identifier,
|
||||
skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f),
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
user.Position + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
}
|
||||
|
||||
//disabled "continuous fabrication" for now
|
||||
//before we enable it, there should be some UI controls for fabricating a specific number of items
|
||||
|
||||
/*var prevFabricatedItem = fabricatedItem;
|
||||
var prevUser = user;
|
||||
CancelFabricating();
|
||||
if (CanBeFabricated(prevFabricatedItem))
|
||||
{
|
||||
//keep fabricating if we can fabricate more
|
||||
StartFabricating(prevFabricatedItem, prevUser, addToServerLog: false);
|
||||
}*/
|
||||
|
||||
|
||||
CancelFabricating();
|
||||
}
|
||||
}
|
||||
@@ -375,7 +399,7 @@ namespace Barotrauma.Items.Components
|
||||
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
|
||||
float average = skillSum / skills.Count;
|
||||
|
||||
return ((average + 100.0f) / 2.0f) / 100.0f;
|
||||
return (average + 100.0f) / 2.0f / 100.0f;
|
||||
}
|
||||
|
||||
public override float GetSkillMultiplier()
|
||||
@@ -390,7 +414,7 @@ namespace Barotrauma.Items.Components
|
||||
private List<Item> GetAvailableIngredients()
|
||||
{
|
||||
List<Item> availableIngredients = new List<Item>();
|
||||
availableIngredients.AddRange(inputContainer.Inventory.Items.Where(it => it != null));
|
||||
availableIngredients.AddRange(inputContainer.Inventory.AllItems);
|
||||
foreach (MapEntity linkedTo in item.linkedTo)
|
||||
{
|
||||
if (linkedTo is Item linkedItem)
|
||||
@@ -404,18 +428,18 @@ namespace Barotrauma.Items.Components
|
||||
itemContainer = deconstructor.OutputContainer;
|
||||
}
|
||||
|
||||
availableIngredients.AddRange(itemContainer.Inventory.Items.Where(it => it != null));
|
||||
availableIngredients.AddRange(itemContainer.Inventory.AllItems);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
if (Character.Controlled?.Inventory != null)
|
||||
{
|
||||
availableIngredients.AddRange(Character.Controlled.Inventory.Items.Distinct().Where(it => it != null));
|
||||
availableIngredients.AddRange(Character.Controlled.Inventory.AllItems);
|
||||
}
|
||||
#else
|
||||
if (user?.Inventory != null)
|
||||
{
|
||||
availableIngredients.AddRange(user.Inventory.Items.Distinct().Where(it => it != null));
|
||||
availableIngredients.AddRange(user.Inventory.AllItems);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -450,9 +474,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else //in another inventory, we need to move the item
|
||||
{
|
||||
if (inputContainer.Inventory.Items.All(it => it != null))
|
||||
if (!inputContainer.Inventory.CanBePut(matchingItem))
|
||||
{
|
||||
var unneededItem = inputContainer.Inventory.Items.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
unneededItem?.Drop(null, createNetworkEvent: !isClient);
|
||||
}
|
||||
inputContainer.Inventory.TryPutItem(matchingItem, user: null, createNetworkEvent: !isClient);
|
||||
|
||||
@@ -31,20 +31,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float pumpSpeedLockTimer, isActiveLockTimer;
|
||||
|
||||
private bool infected;
|
||||
|
||||
[Serialize(false, true, description: "Whether or not the pump is infected with ballast flora spores.")]
|
||||
public bool Infected
|
||||
{
|
||||
get => infected;
|
||||
set
|
||||
{
|
||||
infected = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string InfectIdentifier;
|
||||
|
||||
[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).")]
|
||||
public float FlowPercentage
|
||||
{
|
||||
@@ -116,35 +102,38 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
//less effective when in a bad condition
|
||||
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
|
||||
if (currFlow < 0 && Infected)
|
||||
{
|
||||
InfectBallast(InfectIdentifier);
|
||||
}
|
||||
Infected = false;
|
||||
|
||||
item.CurrentHull.WaterVolume += currFlow;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
||||
}
|
||||
|
||||
public void InfectBallast(string identifier)
|
||||
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)
|
||||
{
|
||||
Hull hull = item.CurrentHull;
|
||||
if (hull == null) { return; }
|
||||
|
||||
// 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 (!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.BallastFlora != null) { return; }
|
||||
|
||||
var ballastFloraPrefab = BallastFloraPrefab.Find(identifier);
|
||||
if (ballastFloraPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to infect a ballast pump (could not find a ballast flora prefab with the identifier \"{identifier}\").\n" + Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 offset = item.WorldPosition - hull.WorldPosition;
|
||||
hull.BallastFlora = new BallastFloraBehavior(hull, BallastFloraPrefab.Find(identifier), offset, firstGrowth: true);
|
||||
hull.BallastFlora = new BallastFloraBehavior(hull, ballastFloraPrefab, offset, firstGrowth: true);
|
||||
|
||||
#if SERVER
|
||||
hull.BallastFlora.SendNetworkMessage(hull.BallastFlora, BallastFloraBehavior.NetworkHeader.Spawn);
|
||||
@@ -180,7 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempTarget))
|
||||
{
|
||||
TargetLevel = MathHelper.Clamp(tempTarget + 50.0f, 0.0f, 100.0f);
|
||||
TargetLevel = MathUtils.InverseLerp(-100.0f, 100.0f, tempTarget) * 100.0f;
|
||||
pumpSpeedLockTimer = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,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)]
|
||||
[Serialize(0.2f, true, 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; }
|
||||
@@ -216,7 +216,7 @@ namespace Barotrauma.Items.Components
|
||||
if (LastAIUser.SelectedConstruction != item && LastAIUser.CanInteractWith(item))
|
||||
{
|
||||
AutoTemp = true;
|
||||
unsentChanges = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
LastAIUser = null;
|
||||
}
|
||||
}
|
||||
@@ -313,12 +313,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (fissionRate > 0.0f)
|
||||
{
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
var containedItems = item.OwnInventory?.AllItems;
|
||||
if (containedItems != null)
|
||||
{
|
||||
foreach (Item item in containedItems)
|
||||
{
|
||||
if (item == null) { continue; }
|
||||
if (!item.HasTag("reactorfuel")) { continue; }
|
||||
item.Condition -= fissionRate / 100.0f * fuelConsumptionRate * deltaTime;
|
||||
}
|
||||
@@ -399,6 +398,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//fission rate is clamped to the amount of available fuel
|
||||
float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f);
|
||||
if (maxFissionRate >= 100.0f) { return false; }
|
||||
|
||||
float maxTurbineOutput = 100.0f;
|
||||
|
||||
//calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max
|
||||
@@ -412,8 +413,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool TooMuchFuel()
|
||||
{
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
if (containedItems != null && containedItems.Count(i => i != null) <= 1) { return false; }
|
||||
var containedItems = item.OwnInventory?.AllItems;
|
||||
if (containedItems != null && containedItems.Count() <= 1) { return false; }
|
||||
|
||||
//get the amount of heat we'd generate if the fission rate was at the low end of the optimal range
|
||||
float minimumHeat = GetGeneratedHeat(optimalFissionRate.X);
|
||||
@@ -530,12 +531,11 @@ namespace Barotrauma.Items.Components
|
||||
fireTimer = 0.0f;
|
||||
meltDownTimer = 0.0f;
|
||||
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
var containedItems = item.OwnInventory?.AllItems;
|
||||
if (containedItems != null)
|
||||
{
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
if (containedItem == null) { continue; }
|
||||
containedItem.Condition = 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -557,58 +557,68 @@ namespace Barotrauma.Items.Components
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
bool shutDown = objective.Option.Equals("shutdown", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
IsActive = true;
|
||||
|
||||
float degreeOfSuccess = DegreeOfSuccess(character);
|
||||
float refuelLimit = 0.3f;
|
||||
//characters with insufficient skill levels don't refuel the reactor
|
||||
if (degreeOfSuccess > refuelLimit)
|
||||
if (!shutDown)
|
||||
{
|
||||
if (objective.SubObjectives.None())
|
||||
float degreeOfSuccess = DegreeOfSuccess(character);
|
||||
float refuelLimit = 0.3f;
|
||||
//characters with insufficient skill levels don't refuel the reactor
|
||||
if (degreeOfSuccess > refuelLimit)
|
||||
{
|
||||
if (!AIDecontainEmptyItems(character, objective, equip: false))
|
||||
if (aiUpdateTimer > 0.0f)
|
||||
{
|
||||
aiUpdateTimer -= deltaTime;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
|
||||
if (aiUpdateTimer > 0.0f)
|
||||
{
|
||||
aiUpdateTimer -= deltaTime;
|
||||
return false;
|
||||
}
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
|
||||
// load more fuel if the current maximum output is only 50% of the current load
|
||||
// or if the fuel rod is (almost) deplenished
|
||||
float minCondition = fuelConsumptionRate * MathUtils.Pow((degreeOfSuccess - refuelLimit) * 2, 2);
|
||||
if (NeedMoreFuel(minimumOutputRatio: 0.5f, minCondition: minCondition))
|
||||
{
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
if (objective.SubObjectives.None())
|
||||
// load more fuel if the current maximum output is only 50% of the current load
|
||||
// or if the fuel rod is (almost) deplenished
|
||||
float minCondition = fuelConsumptionRate * MathUtils.Pow((degreeOfSuccess - refuelLimit) * 2, 2);
|
||||
if (NeedMoreFuel(minimumOutputRatio: 0.5f, minCondition: minCondition))
|
||||
{
|
||||
int itemCount = item.ContainedItems.Count(i => i != null && container.ContainableItems.Any(ri => ri.MatchesItem(i))) + 1;
|
||||
AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == Character.TeamType.FriendlyNPC);
|
||||
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (TooMuchFuel())
|
||||
{
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
if (containedItems != null)
|
||||
{
|
||||
foreach (Item item in containedItems)
|
||||
bool outOfFuel = false;
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
if (item != null && container.ContainableItems.Any(ri => ri.MatchesItem(item)))
|
||||
int itemCount = item.ContainedItems.Count(i => i != null && container.ContainableItems.Any(ri => ri.MatchesItem(i))) + 1;
|
||||
var containObjective = AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC, dropItemOnDeselected: true);
|
||||
containObjective.Completed += ReportFuelRodCount;
|
||||
containObjective.Abandoned += ReportFuelRodCount;
|
||||
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
|
||||
void ReportFuelRodCount()
|
||||
{
|
||||
if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
|
||||
if (!character.IsOnPlayerTeam) { return; }
|
||||
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);
|
||||
outOfFuel = true;
|
||||
}
|
||||
else if (remainingFuelRods < 3)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLowOnFuelRods"), null, 0.0f, "lowonfuelrods", 30.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outOfFuel;
|
||||
}
|
||||
else if (TooMuchFuel())
|
||||
{
|
||||
if (item.OwnInventory?.AllItems != null)
|
||||
{
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
foreach (Item item in item.OwnInventory.AllItemsMod)
|
||||
{
|
||||
if (container.ContainableItems.Any(ri => ri.MatchesItem(item)))
|
||||
{
|
||||
item.Drop(character);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,13 +629,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lastUser != null && lastUser != character && lastUser != LastAIUser)
|
||||
{
|
||||
if (lastUser.SelectedConstruction == item)
|
||||
if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (LastUserWasPlayer)
|
||||
else if (LastUserWasPlayer && lastUser != null && lastUser.TeamID == character.TeamID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -637,48 +647,45 @@ namespace Barotrauma.Items.Components
|
||||
float prevFissionRate = targetFissionRate;
|
||||
float prevTurbineOutput = targetTurbineOutput;
|
||||
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
{
|
||||
case "powerup":
|
||||
PowerOn = true;
|
||||
if (objective.Override || !autoTemp)
|
||||
{
|
||||
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
|
||||
if (degreeOfSuccess < 0.5f)
|
||||
{
|
||||
AutoTemp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoTemp = false;
|
||||
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = FissionRate / 100.0f;
|
||||
TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
|
||||
#endif
|
||||
break;
|
||||
case "shutdown":
|
||||
PowerOn = false;
|
||||
AutoTemp = false;
|
||||
targetFissionRate = 0.0f;
|
||||
targetTurbineOutput = 0.0f;
|
||||
unsentChanges = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (autoTemp != prevAutoTemp ||
|
||||
prevPowerOn != _powerOn ||
|
||||
Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
|
||||
Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
|
||||
if (shutDown)
|
||||
{
|
||||
PowerOn = false;
|
||||
AutoTemp = false;
|
||||
targetFissionRate = 0.0f;
|
||||
targetTurbineOutput = 0.0f;
|
||||
unsentChanges = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PowerOn = true;
|
||||
if (objective.Override || !autoTemp)
|
||||
{
|
||||
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
|
||||
if (degreeOfSuccess < 0.5f)
|
||||
{
|
||||
AutoTemp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoTemp = false;
|
||||
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = FissionRate / 100.0f;
|
||||
TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
|
||||
#endif
|
||||
if (autoTemp != prevAutoTemp ||
|
||||
prevPowerOn != _powerOn ||
|
||||
Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
|
||||
Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
return false;
|
||||
}
|
||||
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
@@ -697,14 +704,14 @@ namespace Barotrauma.Items.Components
|
||||
AutoTemp = false;
|
||||
targetFissionRate = 0.0f;
|
||||
targetTurbineOutput = 0.0f;
|
||||
unsentChanges = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
}
|
||||
break;
|
||||
case "set_fissionrate":
|
||||
if (PowerOn && float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
{
|
||||
targetFissionRate = newFissionRate;
|
||||
unsentChanges = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f;
|
||||
#endif
|
||||
@@ -714,7 +721,7 @@ namespace Barotrauma.Items.Components
|
||||
if (PowerOn && float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
{
|
||||
targetTurbineOutput = newTurbineOutput;
|
||||
unsentChanges = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
#if CLIENT
|
||||
TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f;
|
||||
#endif
|
||||
|
||||
@@ -104,7 +104,8 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Does the sonar have mineral scanning mode?")]
|
||||
[Editable, Serialize(false, false, 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
|
||||
@@ -251,9 +252,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null || !c.Enabled) continue;
|
||||
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue;
|
||||
if (Vector2.DistanceSquared(c.WorldPosition, item.WorldPosition) > range * range) continue;
|
||||
if (c.IsDead || c.Removed || !c.Enabled) { continue; }
|
||||
if (c.AnimController.CurrentHull != null || c.Params.HideInSonar) { continue; }
|
||||
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);
|
||||
if (!targetGroups.ContainsKey(directionName))
|
||||
@@ -276,9 +278,12 @@ namespace Barotrauma.Items.Components
|
||||
dialogTag = "DialogSonarTargetLarge";
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
//prevent the character from reporting other targets in the group
|
||||
for (int i = 1; i < targetGroup.Value.Count; i++)
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Barotrauma.Items.Components
|
||||
private const float AutoPilotMaxSpeed = 0.5f;
|
||||
private const float AIPilotMaxSpeed = 1.0f;
|
||||
|
||||
private Vector2 currVelocity;
|
||||
private Vector2 targetVelocity;
|
||||
|
||||
private Vector2 steeringInput;
|
||||
@@ -52,7 +51,13 @@ namespace Barotrauma.Items.Components
|
||||
private Sonar sonar;
|
||||
|
||||
private Submarine controlledSub;
|
||||
|
||||
|
||||
private bool showIceSpireWarning;
|
||||
|
||||
private List<Submarine> connectedSubs = new List<Submarine>();
|
||||
private const float ConnectedSubUpdateInterval = 1.0f;
|
||||
float connectedSubUpdateTimer;
|
||||
|
||||
public bool AutoPilot
|
||||
{
|
||||
get { return autoPilot; }
|
||||
@@ -67,7 +72,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (pathFinder == null)
|
||||
{
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false)
|
||||
{
|
||||
GetNodePenalty = GetNodePenalty
|
||||
};
|
||||
}
|
||||
MaintainPos = true;
|
||||
if (posToMaintain == null)
|
||||
@@ -87,7 +95,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1.0f, decimals: 3),
|
||||
[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."
|
||||
+ " Can be used to compensate if the ballast tanks are too large/small relative to the size of the submarine.")]
|
||||
public float NeutralBallastLevel
|
||||
@@ -299,6 +307,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
showIceSpireWarning = false;
|
||||
if (user != null && user.Info != null &&
|
||||
user.SelectedConstruction == item &&
|
||||
controlledSub != null && controlledSub.Velocity.LengthSquared() > 0.01f)
|
||||
@@ -323,12 +332,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.SendSignal(0, targetVelocity.X.ToString(CultureInfo.InvariantCulture), "velocity_x_out", user);
|
||||
|
||||
float targetLevel = -targetVelocity.Y;
|
||||
float targetLevel = targetVelocity.X;
|
||||
if (controlledSub != null && controlledSub.FlippedX) { targetLevel *= -1; }
|
||||
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_x_out", user);
|
||||
|
||||
targetLevel = -targetVelocity.Y;
|
||||
targetLevel += (neutralBallastLevel - 0.5f) * 100.0f;
|
||||
|
||||
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", user);
|
||||
}
|
||||
|
||||
@@ -342,7 +352,7 @@ namespace Barotrauma.Items.Components
|
||||
user.Info.IncreaseSkillLevel(
|
||||
"helm",
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime,
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
user.Position + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
|
||||
private void UpdateAutoPilot(float deltaTime)
|
||||
@@ -351,7 +361,8 @@ namespace Barotrauma.Items.Components
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
Vector2 steeringVel = GetSteeringVelocity((Vector2)posToMaintain, 10.0f);
|
||||
TargetVelocity = Vector2.Lerp(TargetVelocity, steeringVel, AutoPilotSteeringLerp);
|
||||
TargetVelocity = Vector2.Lerp(TargetVelocity, steeringVel, AutoPilotSteeringLerp);
|
||||
showIceSpireWarning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -365,9 +376,21 @@ namespace Barotrauma.Items.Components
|
||||
autopilotRecalculatePathTimer = RecalculatePathInterval;
|
||||
}
|
||||
|
||||
if (steeringPath == null) { return; }
|
||||
if (steeringPath == null)
|
||||
{
|
||||
showIceSpireWarning = false;
|
||||
return;
|
||||
}
|
||||
steeringPath.CheckProgress(ConvertUnits.ToSimUnits(controlledSub.WorldPosition), 10.0f);
|
||||
|
||||
connectedSubUpdateTimer -= deltaTime;
|
||||
if (connectedSubUpdateTimer <= 0.0f)
|
||||
{
|
||||
connectedSubs.Clear();
|
||||
connectedSubs = controlledSub?.GetConnectedSubs();
|
||||
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
||||
}
|
||||
|
||||
if (autopilotRayCastTimer <= 0.0f && steeringPath.NextNode != null)
|
||||
{
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(steeringPath.NextNode.Position - controlledSub.WorldPosition);
|
||||
@@ -417,27 +440,38 @@ namespace Barotrauma.Items.Components
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.Y), controlledSub.Borders.Height * 0.75f));
|
||||
|
||||
float avoidRadius = avoidDist.Length();
|
||||
float damagingWallAvoidRadius = avoidRadius * 1.5f;
|
||||
float damagingWallAvoidRadius = MathHelper.Clamp(avoidRadius * 1.5f, 5000.0f, 10000.0f);
|
||||
|
||||
Vector2 newAvoidStrength = Vector2.Zero;
|
||||
|
||||
debugDrawObstacles.Clear();
|
||||
|
||||
//steer away from nearby walls
|
||||
showIceSpireWarning = false;
|
||||
var closeCells = Level.Loaded.GetCells(controlledSub.WorldPosition, 4);
|
||||
foreach (VoronoiCell cell in closeCells)
|
||||
{
|
||||
if (Level.Loaded?.ExtraWalls.Any(w => w.WallDamageOnTouch > 0.0f && w.Cells.Contains(cell)) ?? false)
|
||||
if (cell.DoesDamage)
|
||||
{
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
Vector2 closestPoint = MathUtils.GetClosestPointOnLineSegment(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition);
|
||||
float dist = Vector2.Distance(closestPoint, controlledSub.WorldPosition);
|
||||
Vector2 diff = closestPoint - controlledSub.WorldPosition;
|
||||
float dist = diff.Length() - Math.Max(controlledSub.Borders.Width, controlledSub.Borders.Height) / 2;
|
||||
if (dist > damagingWallAvoidRadius) { continue; }
|
||||
Vector2 diff = controlledSub.WorldPosition - cell.Center;
|
||||
Vector2 avoid = Vector2.Normalize(diff) * (damagingWallAvoidRadius - dist) / damagingWallAvoidRadius;
|
||||
|
||||
Vector2 normalizedDiff = Vector2.Normalize(diff);
|
||||
float dot = Vector2.Dot(normalizedDiff, controlledSub.Velocity);
|
||||
|
||||
float avoidStrength = MathHelper.Clamp(MathHelper.Lerp(1.0f, 0.0f, dist / damagingWallAvoidRadius - dot), 0.0f, 1.0f);
|
||||
Vector2 avoid = -normalizedDiff * avoidStrength;
|
||||
newAvoidStrength += avoid;
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, edge.Center, 1.0f, avoid, cell.Translation));
|
||||
|
||||
if (dot > 0.0f)
|
||||
{
|
||||
showIceSpireWarning = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -453,7 +487,7 @@ namespace Barotrauma.Items.Components
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero, Vector2.Zero));
|
||||
continue;
|
||||
}
|
||||
if (diff.LengthSquared() < 1.0f) diff = Vector2.UnitY;
|
||||
if (diff.LengthSquared() < 1.0f) { diff = Vector2.UnitY; }
|
||||
|
||||
Vector2 normalizedDiff = Vector2.Normalize(diff);
|
||||
float dot = controlledSub.Velocity == Vector2.Zero ?
|
||||
@@ -480,8 +514,7 @@ namespace Barotrauma.Items.Components
|
||||
//steer away from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub == controlledSub) { continue; }
|
||||
if (controlledSub.DockedTo.Contains(sub)) { continue; }
|
||||
if (sub == controlledSub || connectedSubs.Contains(sub)) { continue; }
|
||||
Point sizeSum = controlledSub.Borders.Size + sub.Borders.Size;
|
||||
Vector2 minDist = sizeSum.ToVector2() / 2;
|
||||
Vector2 diff = controlledSub.WorldPosition - sub.WorldPosition;
|
||||
@@ -512,6 +545,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private float? GetNodePenalty(PathNode node, PathNode nextNode)
|
||||
{
|
||||
if (node.Waypoint?.Tunnel == null || controlledSub == null || node.Waypoint.Tunnel.Type == Level.TunnelType.MainPath) { return 0.0f; }
|
||||
//never navigate from the main path to another type of path
|
||||
if (node.Waypoint.Tunnel.Type == Level.TunnelType.MainPath && nextNode.Waypoint?.Tunnel?.Type != Level.TunnelType.MainPath) { return null; }
|
||||
//higher cost for side paths (= autopilot prefers the main path, but can still navigate side paths if it ends up on one)
|
||||
return 1000.0f;
|
||||
}
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (Level.Loaded == null) { return; }
|
||||
@@ -584,7 +626,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (objective.Override)
|
||||
{
|
||||
if (user != character && user != null && user.SelectedConstruction == item)
|
||||
if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
|
||||
}
|
||||
@@ -647,6 +689,10 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
sonar?.AIOperate(deltaTime, character, objective);
|
||||
if (!MaintainPos && showIceSpireWarning && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -654,7 +700,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (connection.Name == "velocity_in")
|
||||
{
|
||||
currVelocity = XMLExtensions.ParseVector2(signal, false);
|
||||
TargetVelocity = XMLExtensions.ParseVector2(signal, errorMessages: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user