v0.12.0.2
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 || ic.RemoveContainedItemsOnDeconstruct) { 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);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,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 +161,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 +190,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 +298,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 +332,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 +392,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 +407,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 +421,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 +467,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);
|
||||
|
||||
@@ -102,7 +102,7 @@ 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
|
||||
@@ -112,13 +112,16 @@ namespace Barotrauma.Items.Components
|
||||
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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -414,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);
|
||||
@@ -532,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;
|
||||
}
|
||||
}
|
||||
@@ -559,55 +557,58 @@ 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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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, dropItemOnDeselected: true);
|
||||
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)
|
||||
if (!AIDecontainEmptyItems(character, objective, equip: false))
|
||||
{
|
||||
if (item != null && container.ContainableItems.Any(ri => ri.MatchesItem(item)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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 == CharacterTeamType.FriendlyNPC, dropItemOnDeselected: true);
|
||||
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (TooMuchFuel())
|
||||
{
|
||||
if (item.OwnInventory?.AllItems != null)
|
||||
{
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
foreach (Item item in item.OwnInventory.AllItemsMod)
|
||||
{
|
||||
item.Drop(character);
|
||||
break;
|
||||
if (container.ContainableItems.Any(ri => ri.MatchesItem(item)))
|
||||
{
|
||||
item.Drop(character);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,7 +625,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (LastUserWasPlayer)
|
||||
else if (LastUserWasPlayer && lastUser != null && lastUser.TeamID == character.TeamID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -636,48 +637,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()
|
||||
@@ -696,14 +694,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
|
||||
@@ -713,7 +711,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
|
||||
|
||||
@@ -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; }
|
||||
@@ -302,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)
|
||||
@@ -326,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);
|
||||
}
|
||||
|
||||
@@ -345,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)
|
||||
@@ -354,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;
|
||||
}
|
||||
|
||||
@@ -368,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);
|
||||
@@ -420,13 +440,14 @@ 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)
|
||||
{
|
||||
@@ -435,12 +456,22 @@ namespace Barotrauma.Items.Components
|
||||
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;
|
||||
}
|
||||
@@ -456,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 ?
|
||||
@@ -483,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;
|
||||
@@ -659,6 +689,10 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
sonar?.AIOperate(deltaTime, character, objective);
|
||||
if (!MaintainPos && showIceSpireWarning)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -666,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