v1.1.19.3 (Treacherous Tides Hotfix 2)
This commit is contained in:
@@ -167,10 +167,6 @@ namespace Barotrauma
|
||||
|
||||
public HumanAIController(Character c) : base(c)
|
||||
{
|
||||
if (!c.IsHuman)
|
||||
{
|
||||
throw new Exception($"Tried to create a human ai controller for a non-human: {c.SpeciesName}!");
|
||||
}
|
||||
insideSteering = new IndoorsSteeringManager(this, true, false);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
|
||||
@@ -1223,19 +1223,15 @@ namespace Barotrauma
|
||||
public static Character Create(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||
{
|
||||
Character newCharacter = null;
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName)
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName || hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new EnemyAIController(aiCharacter, seed);
|
||||
|
||||
var ai = (prefab.Identifier == CharacterPrefab.HumanSpeciesName || aiCharacter.Params.UseHumanAI) ?
|
||||
new HumanAIController(aiCharacter) as AIController :
|
||||
new EnemyAIController(aiCharacter, seed);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else if (hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new HumanAIController(aiCharacter);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4656,7 +4652,7 @@ namespace Barotrauma
|
||||
}
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
|
||||
|
||||
public void Revive(bool removeAfflictions = true)
|
||||
public void Revive(bool removeAfflictions = true, bool createNetworkEvent = false)
|
||||
{
|
||||
if (Removed)
|
||||
{
|
||||
@@ -4705,7 +4701,11 @@ namespace Barotrauma
|
||||
limb.IsSevered = false;
|
||||
}
|
||||
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
if (createNetworkEvent && GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new CharacterStatusEventData());
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
|
||||
@@ -148,11 +148,6 @@ namespace Barotrauma
|
||||
return minVitality;
|
||||
}
|
||||
return vitality;
|
||||
|
||||
}
|
||||
private set
|
||||
{
|
||||
vitality = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +249,7 @@ namespace Barotrauma
|
||||
public CharacterHealth(Character character)
|
||||
{
|
||||
this.Character = character;
|
||||
Vitality = 100.0f;
|
||||
vitality = 100.0f;
|
||||
|
||||
DoesBleed = true;
|
||||
UseHealthWindow = false;
|
||||
@@ -271,7 +266,7 @@ namespace Barotrauma
|
||||
this.Character = character;
|
||||
InitIrremovableAfflictions();
|
||||
|
||||
Vitality = UnmodifiedMaxVitality;
|
||||
vitality = UnmodifiedMaxVitality;
|
||||
|
||||
minVitality = character.IsHuman ? -100.0f : 0.0f;
|
||||
|
||||
@@ -971,7 +966,7 @@ namespace Barotrauma
|
||||
|
||||
public void CalculateVitality()
|
||||
{
|
||||
Vitality = MaxVitality;
|
||||
vitality = MaxVitality;
|
||||
IsParalyzed = false;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
@@ -984,7 +979,7 @@ namespace Barotrauma
|
||||
{
|
||||
vitalityDecrease *= GetVitalityMultiplier(affliction, limbHealth);
|
||||
}
|
||||
Vitality -= vitalityDecrease;
|
||||
vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
|
||||
if (affliction.Strength >= affliction.Prefab.MaxStrength &&
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature live without water or does it die on dry land?"), Editable]
|
||||
public bool NeedsWater { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Note: non-humans with a human AI aren't fully supported. Enabling this on a non-human character may lead to issues.")]
|
||||
public bool UseHumanAI { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is this creature an artificial creature, like robot or machine that shouldn't be affected by afflictions that affect only organic creatures? Overrides DoesBleed."), Editable]
|
||||
public bool IsMachine { get; set; }
|
||||
|
||||
|
||||
@@ -36,7 +36,19 @@ namespace Barotrauma
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
private bool targetNotFound = false;
|
||||
/// <summary>
|
||||
/// If the action tags some entities directly (not trying to find targets on the fly),
|
||||
/// we may be able to determine that targets can not be found even if we'd recheck
|
||||
/// </summary>
|
||||
private bool cantFindTargets = false;
|
||||
|
||||
/// <summary>
|
||||
/// If the TagAction adds a target predicate (a criteria that keeps finding targets on the fly),
|
||||
/// we must keep checking if targets have been found to determine if the action can continue or not
|
||||
/// </summary>
|
||||
private bool mustRecheckTargets = false;
|
||||
|
||||
private bool taggingDone = false;
|
||||
|
||||
public TagAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
@@ -198,6 +210,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(tag, predicate);
|
||||
mustRecheckTargets = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +218,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
if (ChoosePercentage > 0.0f)
|
||||
@@ -230,7 +243,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +263,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
ParentEvent.AddTarget(tag, entities.GetRandomUnsynced());
|
||||
@@ -260,26 +273,29 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished || targetNotFound) { return; }
|
||||
if (isFinished || cantFindTargets) { return; }
|
||||
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
|
||||
targetNotFound = false;
|
||||
foreach (string entry in criteriaSplit)
|
||||
if (!taggingDone)
|
||||
{
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
cantFindTargets = false;
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
foreach (string entry in criteriaSplit)
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
}
|
||||
}
|
||||
taggingDone = true;
|
||||
}
|
||||
|
||||
if (ContinueIfNoTargetsFound)
|
||||
@@ -288,7 +304,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !targetNotFound;
|
||||
if (mustRecheckTargets)
|
||||
{
|
||||
isFinished = ParentEvent.GetTargets(Tag).Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !cantFindTargets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1353,6 +1353,7 @@ namespace Barotrauma
|
||||
if (item.HiddenInGame) { continue; }
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
if (item.Prefab.DontTransferBetweenSubs) { continue; }
|
||||
if (AnyParentInventoryDisableTransfer(item)) { continue; }
|
||||
var rootOwner = item.GetRootInventoryOwner();
|
||||
if (rootOwner is Character) { continue; }
|
||||
if (rootOwner is Item ownerItem && (ownerItem.NonInteractable || item.NonPlayerTeamInteractable || ownerItem.HiddenInGame)) { continue; }
|
||||
@@ -1362,6 +1363,15 @@ namespace Barotrauma
|
||||
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
|
||||
itemsToTransfer.Add((item, item.Container));
|
||||
item.Submarine = null;
|
||||
|
||||
static bool AnyParentInventoryDisableTransfer(Item item)
|
||||
{
|
||||
if (item.ParentInventory?.Owner is not Item parentOwner) { return false; }
|
||||
return HasProblematicComponent(parentOwner) || AnyParentInventoryDisableTransfer(parentOwner);
|
||||
|
||||
static bool HasProblematicComponent(Item it)
|
||||
=> it.Components.Any(static c => c.DontTransferInventoryBetweenSubs);
|
||||
}
|
||||
}
|
||||
foreach (var (item, container) in itemsToTransfer)
|
||||
{
|
||||
@@ -1369,8 +1379,15 @@ namespace Barotrauma
|
||||
{
|
||||
// Drop the item if it's not inside another item set to be transferred.
|
||||
item.Drop(null, createNetworkEvent: false, setTransform: false);
|
||||
//dropping items sets the sub, set it back to null
|
||||
item.Submarine = null;
|
||||
foreach (var itemContainer in item.GetComponents<ItemContainer>())
|
||||
{
|
||||
itemContainer.Inventory.FindAllItems((_) => true, recursive: true).ForEach(it => it.Submarine = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(itemsToTransfer.None(it => it.item.Submarine != null), "Item that was set to be transferred was not removed from the sub!");
|
||||
currentSub.Info.NoItems = true;
|
||||
}
|
||||
// Serialize the current sub
|
||||
@@ -1408,6 +1425,7 @@ namespace Barotrauma
|
||||
{
|
||||
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true, allowConnectedSubs: true);
|
||||
}
|
||||
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
|
||||
if (item.Container == null && (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null, createNetworkEvent: false)))
|
||||
{
|
||||
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, spawnHull, ref availableContainers);
|
||||
@@ -1416,13 +1434,16 @@ namespace Barotrauma
|
||||
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
|
||||
item.SetTransform(simPos, 0.0f, findNewHull: false, setPrevTransform: false);
|
||||
}
|
||||
else if (cargoContainer.Item.Submarine is Submarine containerSub)
|
||||
else
|
||||
{
|
||||
// Use the item's sub in case the sub consists of multiple linked subs.
|
||||
item.Submarine = containerSub;
|
||||
if (cargoContainer.Item.Submarine is Submarine containerSub)
|
||||
{
|
||||
// Use the item's sub in case the sub consists of multiple linked subs.
|
||||
item.Submarine = containerSub;
|
||||
}
|
||||
newContainerName = cargoContainer.Item.Prefab.Identifier.ToString();
|
||||
}
|
||||
}
|
||||
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
|
||||
string msg = "Item transfer log error.";
|
||||
if (oldContainer != null)
|
||||
{
|
||||
|
||||
@@ -71,6 +71,12 @@ namespace Barotrauma.Items.Components
|
||||
protected const float CorrectionDelay = 1.0f;
|
||||
protected CoroutineHandle delayedCorrectionCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, the contents of the item are not transferred when the player transfers items between subs.
|
||||
/// Use this if this component uses item containers in a way where removing the item from the container via external means would cause problems.
|
||||
/// </summary>
|
||||
public virtual bool DontTransferInventoryBetweenSubs => false;
|
||||
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How long it takes to pick up the item (in seconds).")]
|
||||
public float PickingTime
|
||||
{
|
||||
|
||||
@@ -416,7 +416,7 @@ namespace Barotrauma.Items.Components
|
||||
if (requiredItem.UseCondition && suitableIngredient.ConditionPercentage - requiredItem.MinCondition * 100 > 0.0f)
|
||||
{
|
||||
suitableIngredient.Condition -= suitableIngredient.Prefab.Health * requiredItem.MinCondition;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (suitableIngredient.OwnInventory != null)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private List<LightComponent>? lightComponents;
|
||||
|
||||
// We don't want the seeds to be transferred to a new submarine as seeds are not supposed to leave the container after they have been planted.
|
||||
public override bool DontTransferInventoryBetweenSubs => true;
|
||||
|
||||
public Planter(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
canBePicked = true;
|
||||
|
||||
@@ -26,19 +26,37 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool IsActive => true;
|
||||
|
||||
// We don't want the components and wires to transfer between subs as it would cause issues.
|
||||
public override bool DontTransferInventoryBetweenSubs => true;
|
||||
|
||||
public Option<CircuitBoxConnection> FindInputOutputConnection(Identifier connectionName)
|
||||
{
|
||||
foreach (CircuitBoxInputConnection input in Inputs)
|
||||
{
|
||||
if (input.Name != connectionName) { continue; }
|
||||
|
||||
return Option.Some<CircuitBoxConnection>(input);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxOutputConnection output in Outputs)
|
||||
{
|
||||
if (output.Name != connectionName) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(output);
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public Option<CircuitBoxConnection> FindInputOutputConnection(Connection connection)
|
||||
{
|
||||
foreach (CircuitBoxInputConnection input in Inputs)
|
||||
{
|
||||
if (input.Connection != connection) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(input);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxOutputConnection output in Outputs)
|
||||
{
|
||||
if (output.Connection != connection) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(output);
|
||||
}
|
||||
|
||||
@@ -338,7 +356,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnItem(this, prefab, WireContainer, wire =>
|
||||
SpawnItem(prefab, user: null, container: WireContainer, onSpawned: wire =>
|
||||
{
|
||||
AddWireDirect(wireId, prefab, Option.Some(wire), one, two);
|
||||
onItemSpawned(wire);
|
||||
@@ -359,7 +377,7 @@ namespace Barotrauma.Items.Components
|
||||
private void AddWireDirect(ushort id, ItemPrefab prefab, Option<Item> backingItem, CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
=> Wires.Add(new CircuitBoxWire(this, id, backingItem, one, two, prefab));
|
||||
|
||||
private bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, Action<Item> onItemSpawned)
|
||||
private bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, Character? user, Action<Item>? onItemSpawned)
|
||||
{
|
||||
if (id is ICircuitBoxIdentifiable.NullComponentID)
|
||||
{
|
||||
@@ -373,10 +391,10 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
SpawnItem(this, prefab, ComponentContainer, spawnedItem =>
|
||||
SpawnItem(prefab, user, ComponentContainer, spawnedItem =>
|
||||
{
|
||||
Components.Add(new CircuitBoxComponent(id, spawnedItem, pos, this, usedResource));
|
||||
onItemSpawned(spawnedItem);
|
||||
onItemSpawned?.Invoke(spawnedItem);
|
||||
});
|
||||
|
||||
OnViewUpdateProjSpecific();
|
||||
@@ -646,7 +664,7 @@ namespace Barotrauma.Items.Components
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public static void SpawnItem(CircuitBox circuitBox, ItemPrefab prefab, ItemContainer? container, Action<Item> onSpawned)
|
||||
public static void SpawnItem(ItemPrefab prefab, Character? user, ItemContainer? container, Action<Item> onSpawned)
|
||||
{
|
||||
if (container is null)
|
||||
{
|
||||
@@ -655,13 +673,27 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (IsInGame())
|
||||
{
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, container.Inventory, onSpawned: onSpawned);
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, container.Inventory, onSpawned: it =>
|
||||
{
|
||||
AssignWifiComponentTeam(it, user);
|
||||
onSpawned(it);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Item forceSpawnedItem = new Item(prefab, Vector2.Zero, null);
|
||||
container.Inventory.TryPutItem(forceSpawnedItem, null);
|
||||
onSpawned(forceSpawnedItem);
|
||||
AssignWifiComponentTeam(forceSpawnedItem, user);
|
||||
|
||||
static void AssignWifiComponentTeam(Item item, Character? user)
|
||||
{
|
||||
if (user == null) { return; }
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
wifiComponent.TeamID = user.TeamID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveItem(Item item)
|
||||
|
||||
@@ -98,11 +98,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="maxStackSize">Defaults to <see cref="ItemPrefab.MaxStackSize"/> if null</param>
|
||||
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null, float? condition = null)
|
||||
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null, float? condition = null, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (itemPrefab == null) { return 0; }
|
||||
maxStackSize ??= itemPrefab.GetMaxStackSize(inventory);
|
||||
if (items.Count > 0)
|
||||
if (items.Count > 0 && !ignoreItemsInSlot)
|
||||
{
|
||||
if (condition.HasValue)
|
||||
{
|
||||
@@ -517,10 +517,10 @@ namespace Barotrauma
|
||||
return count;
|
||||
}
|
||||
|
||||
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, condition: condition);
|
||||
return slots[i].HowManyCanBePut(itemPrefab, condition: condition, ignoreItemsInSlot: ignoreItemsInSlot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -862,7 +862,25 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return ownInventory?.AllItems ?? Enumerable.Empty<Item>();
|
||||
if (OwnInventories.Length < 2)
|
||||
{
|
||||
if (OwnInventory == null) { yield break; }
|
||||
|
||||
foreach (var item in OwnInventory.AllItems)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var inventory in OwnInventories)
|
||||
{
|
||||
foreach (var item in inventory.AllItems)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,6 +889,8 @@ namespace Barotrauma
|
||||
get { return ownInventory; }
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<ItemInventory> OwnInventories = ImmutableArray<ItemInventory>.Empty;
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description:
|
||||
"Enable if you want to display the item HUD side by side with another item's HUD, when linked together. " +
|
||||
"Disclaimer: It's possible or even likely that the views block each other, if they were not designed to be viewed together!")]
|
||||
@@ -1192,6 +1212,8 @@ namespace Barotrauma
|
||||
ownInventory = itemContainer.Inventory;
|
||||
}
|
||||
|
||||
OwnInventories = GetComponents<ItemContainer>().Select(ic => ic.Inventory).ToImmutableArray();
|
||||
|
||||
qualityComponent = GetComponent<Quality>();
|
||||
|
||||
IsLadder = GetComponent<Ladder>() != null;
|
||||
@@ -2533,8 +2555,7 @@ namespace Barotrauma
|
||||
foreach (Connection c in connectionPanel.Connections)
|
||||
{
|
||||
if (connectionFilter != null && !connectionFilter.Invoke(c)) { continue; }
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
foreach (Connection recipient in c.Recipients)
|
||||
{
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
if (component != null && !connectedComponents.Contains(component))
|
||||
@@ -2587,9 +2608,20 @@ namespace Barotrauma
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents, bool ignoreInactiveRelays, bool allowTraversingBackwards = true) where T : ItemComponent
|
||||
{
|
||||
alreadySearched.Add(c);
|
||||
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
static IEnumerable<Connection> GetRecipients(Connection c)
|
||||
{
|
||||
foreach (Connection recipient in c.Recipients)
|
||||
{
|
||||
yield return recipient;
|
||||
}
|
||||
//check circuit box inputs/outputs this connection is connected to
|
||||
foreach (var circuitBoxConnection in c.CircuitBoxConnections)
|
||||
{
|
||||
yield return circuitBoxConnection.Connection;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Connection recipient in GetRecipients(c))
|
||||
{
|
||||
if (alreadySearched.Contains(recipient)) { continue; }
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
@@ -2598,23 +2630,53 @@ namespace Barotrauma
|
||||
connectedComponents.Add(component);
|
||||
}
|
||||
|
||||
//connected to a wifi component -> see which other wifi components it can communicate with
|
||||
var wifiComponent = recipient.Item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null && wifiComponent.CanTransmit())
|
||||
var circuitBox = recipient.Item.GetComponent<CircuitBox>();
|
||||
if (circuitBox != null)
|
||||
{
|
||||
foreach (var wifiReceiver in wifiComponent.GetTransmittersInRange())
|
||||
//if this is a circuit box, check what the connection is connected to inside the box
|
||||
var potentialCbConnection = circuitBox.FindInputOutputConnection(recipient);
|
||||
if (potentialCbConnection.TryUnwrap(out var cbConnection))
|
||||
{
|
||||
var receiverConnections = wifiReceiver.Item.Connections;
|
||||
if (receiverConnections == null) { continue; }
|
||||
foreach (Connection wifiOutput in receiverConnections)
|
||||
if (cbConnection is CircuitBoxInputConnection inputConnection)
|
||||
{
|
||||
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
||||
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
foreach (var connectedTo in inputConnection.ExternallyConnectedTo)
|
||||
{
|
||||
if (alreadySearched.Contains(connectedTo.Connection)) { continue; }
|
||||
CheckRecipient(connectedTo.Connection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var connectedFrom in cbConnection.ExternallyConnectedFrom)
|
||||
{
|
||||
if (alreadySearched.Contains(connectedFrom.Connection) || !allowTraversingBackwards) { continue; }
|
||||
CheckRecipient(connectedFrom.Connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckRecipient(recipient);
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
void CheckRecipient(Connection recipient)
|
||||
{
|
||||
//connected to a wifi component -> see which other wifi components it can communicate with
|
||||
var wifiComponent = recipient.Item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null && wifiComponent.CanTransmit())
|
||||
{
|
||||
foreach (var wifiReceiver in wifiComponent.GetTransmittersInRange())
|
||||
{
|
||||
var receiverConnections = wifiReceiver.Item.Connections;
|
||||
if (receiverConnections == null) { continue; }
|
||||
foreach (Connection wifiOutput in receiverConnections)
|
||||
{
|
||||
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
||||
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreInactiveRelays)
|
||||
|
||||
@@ -58,12 +58,12 @@ namespace Barotrauma
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition, quality) && slots[i].Items.Count < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (itemPrefab == null) { return 0; }
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.GetMaxStackSize(this), container.GetMaxStackSize(i)), condition);
|
||||
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.GetMaxStackSize(this), container.GetMaxStackSize(i)), condition, ignoreItemsInSlot);
|
||||
}
|
||||
|
||||
public override bool IsFull(bool takeStacksIntoAccount = false)
|
||||
|
||||
@@ -843,7 +843,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private int maxStackSizeCharacterInventory;
|
||||
[Serialize(-1, IsPropertySaveable.No)]
|
||||
[Serialize(-1, IsPropertySaveable.No, description: "Maximum stack size when the item is in a character inventory.")]
|
||||
public int MaxStackSizeCharacterInventory
|
||||
{
|
||||
get { return maxStackSizeCharacterInventory; }
|
||||
@@ -851,7 +851,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private int maxStackSizeHoldableOrWearableInventory;
|
||||
[Serialize(-1, IsPropertySaveable.No)]
|
||||
[Serialize(-1, IsPropertySaveable.No, description:
|
||||
"Maximum stack size when the item is inside a holdable or wearable item. "+
|
||||
"If not set, defaults to MaxStackSizeCharacterInventory.")]
|
||||
public int MaxStackSizeHoldableOrWearableInventory
|
||||
{
|
||||
get { return maxStackSizeHoldableOrWearableInventory; }
|
||||
@@ -864,15 +866,20 @@ namespace Barotrauma
|
||||
{
|
||||
return maxStackSizeCharacterInventory;
|
||||
}
|
||||
else if (maxStackSizeHoldableOrWearableInventory > 0 &&
|
||||
inventory?.Owner is Item item && (item.GetComponent<Holdable>() != null || item.GetComponent<Wearable>() != null))
|
||||
else if (inventory?.Owner is Item item &&
|
||||
(item.GetComponent<Holdable>() is { Attachable: false } || item.GetComponent<Wearable>() != null))
|
||||
{
|
||||
return maxStackSizeHoldableOrWearableInventory;
|
||||
}
|
||||
else
|
||||
{
|
||||
return maxStackSize;
|
||||
if (maxStackSizeHoldableOrWearableInventory > 0)
|
||||
{
|
||||
return maxStackSizeHoldableOrWearableInventory;
|
||||
}
|
||||
else if (maxStackSizeCharacterInventory > 0)
|
||||
{
|
||||
//if maxStackSizeHoldableOrWearableInventory is not set, it defaults to maxStackSizeCharacterInventory
|
||||
return maxStackSizeCharacterInventory;
|
||||
}
|
||||
}
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
@@ -880,7 +887,7 @@ namespace Barotrauma
|
||||
|
||||
public ImmutableHashSet<Identifier> AllowDroppingOnSwapWith { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
[Serialize(false, IsPropertySaveable.No, "If enabled, the item is not transferred when the player transfers items between subs.")]
|
||||
public bool DontTransferBetweenSubs { get; private set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
@@ -1032,6 +1039,7 @@ namespace Barotrauma
|
||||
var levelCommonness = new Dictionary<Identifier, CommonnessInfo>();
|
||||
var levelQuantity = new Dictionary<Identifier, FixedQuantityResourceInfo>();
|
||||
|
||||
List<FabricationRecipe> loadedRecipes = new List<FabricationRecipe>();
|
||||
foreach (ContentXElement subElement in ConfigElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -1114,9 +1122,10 @@ namespace Barotrauma
|
||||
var newRecipe = new FabricationRecipe(subElement, Identifier);
|
||||
if (fabricationRecipes.TryGetValue(newRecipe.RecipeHash, out var prevRecipe))
|
||||
{
|
||||
int prevRecipeIndex = loadedRecipes.IndexOf(prevRecipe);
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in item prefab \"{ToString()}\": " +
|
||||
$"{prevRecipe.TargetItemPrefabIdentifier} has the same hash as {newRecipe.TargetItemPrefabIdentifier}. " +
|
||||
$"Fabrication recipe #{loadedRecipes.Count + 1} has the same hash as recipe #{prevRecipeIndex + 1}. This is most likely caused by identical, duplicate recipes." +
|
||||
$"This will cause issues with fabrication."
|
||||
);
|
||||
}
|
||||
@@ -1124,6 +1133,7 @@ namespace Barotrauma
|
||||
{
|
||||
fabricationRecipes.Add(newRecipe.RecipeHash, newRecipe);
|
||||
}
|
||||
loadedRecipes.Add(newRecipe);
|
||||
break;
|
||||
case "preferredcontainer":
|
||||
var preferredContainer = new PreferredContainer(subElement);
|
||||
|
||||
@@ -130,10 +130,17 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// When set to true, the explosion don't deal less damage when the target is behind a solid object.
|
||||
/// </summary>
|
||||
public bool IgnoreCover
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool IgnoreCover { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the damage from the explosion decrease with distance from the origin of the explosion?
|
||||
/// </summary>
|
||||
public bool DistanceFalloff { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Structures that don't count as "cover" that reduces damage from the explosion. Only relevant if IgnoreCover is set to false.
|
||||
/// </summary>
|
||||
public IEnumerable<Structure> IgnoredCover;
|
||||
|
||||
/// <summary>
|
||||
/// How long the light source created by the explosion lasts.
|
||||
@@ -311,12 +318,15 @@ namespace Barotrauma
|
||||
|
||||
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
|
||||
{
|
||||
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker, IgnoredSubmarines, Attack.EmitStructureDamageParticles);
|
||||
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker,
|
||||
IgnoredSubmarines,
|
||||
Attack.EmitStructureDamageParticles,
|
||||
DistanceFalloff);
|
||||
}
|
||||
|
||||
if (BallastFloraDamage > 0.0f)
|
||||
{
|
||||
RangedBallastFloraDamage(worldPosition, displayRange, BallastFloraDamage, attacker);
|
||||
RangedBallastFloraDamage(worldPosition, displayRange, BallastFloraDamage, attacker, DistanceFalloff);
|
||||
}
|
||||
|
||||
if (EmpStrength > 0.0f)
|
||||
@@ -326,7 +336,7 @@ namespace Barotrauma
|
||||
{
|
||||
float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
|
||||
if (distSqr > displayRangeSqr) { continue; }
|
||||
float distFactor = CalculateDistanceFactor(distSqr, displayRange);
|
||||
float distFactor = DistanceFalloff ? CalculateDistanceFactor(distSqr, displayRange) : 1.0f;
|
||||
|
||||
//damage repairable power-consuming items
|
||||
var powered = item.GetComponent<Powered>();
|
||||
@@ -362,7 +372,10 @@ namespace Barotrauma
|
||||
float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
|
||||
if (distSqr > displayRangeSqr) { continue; }
|
||||
|
||||
float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - (float)Math.Sqrt(distSqr) / displayRange :
|
||||
1.0f;
|
||||
//repair repairable items
|
||||
if (item.Repairables.Any())
|
||||
{
|
||||
@@ -415,13 +428,16 @@ namespace Barotrauma
|
||||
|
||||
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
|
||||
{
|
||||
float distFactor = 1.0f - dist / displayRange;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - dist / displayRange :
|
||||
1.0f;
|
||||
float damageAmount = Attack.GetItemDamage(1.0f, item.Prefab.ExplosionDamageMultiplier);
|
||||
|
||||
Vector2 explosionPos = worldPosition;
|
||||
if (item.Submarine != null) { explosionPos -= item.Submarine.Position; }
|
||||
|
||||
damageAmount *= GetObstacleDamageMultiplier(ConvertUnits.ToSimUnits(explosionPos), worldPosition, item.SimPosition);
|
||||
damageAmount *= GetObstacleDamageMultiplier(ConvertUnits.ToSimUnits(explosionPos), worldPosition, item.SimPosition, IgnoredCover);
|
||||
item.Condition -= damageAmount * distFactor;
|
||||
}
|
||||
}
|
||||
@@ -482,12 +498,15 @@ namespace Barotrauma
|
||||
|
||||
if (dist > attack.Range) { continue; }
|
||||
|
||||
float distFactor = 1.0f - dist / attack.Range;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - dist / attack.Range :
|
||||
1.0f;
|
||||
|
||||
//solid obstacles between the explosion and the limb reduce the effect of the explosion
|
||||
if (!IgnoreCover)
|
||||
{
|
||||
distFactor *= GetObstacleDamageMultiplier(explosionPos, worldPosition, limb.SimPosition);
|
||||
distFactor *= GetObstacleDamageMultiplier(explosionPos, worldPosition, limb.SimPosition, IgnoredCover);
|
||||
}
|
||||
if (distFactor > 0)
|
||||
{
|
||||
@@ -602,7 +621,8 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
|
||||
/// </summary>
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null, bool emitWallDamageParticles = true)
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null,
|
||||
bool emitWallDamageParticles = true, bool distanceFalloff = true)
|
||||
{
|
||||
float dist = 600.0f;
|
||||
damagedStructures.Clear();
|
||||
@@ -616,7 +636,10 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < structure.SectionCount; i++)
|
||||
{
|
||||
float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
|
||||
float distFactor =
|
||||
distanceFalloff ?
|
||||
1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange) :
|
||||
1.0f;
|
||||
if (distFactor <= 0.0f) { continue; }
|
||||
|
||||
structure.AddDamage(i, damage * distFactor, attacker, emitParticles: emitWallDamageParticles);
|
||||
@@ -680,7 +703,7 @@ namespace Barotrauma
|
||||
return damagedStructures;
|
||||
}
|
||||
|
||||
public static void RangedBallastFloraDamage(Vector2 worldPosition, float worldRange, float damage, Character attacker = null)
|
||||
public static void RangedBallastFloraDamage(Vector2 worldPosition, float worldRange, float damage, Character attacker = null, bool distanceFalloff = true)
|
||||
{
|
||||
List<BallastFloraBehavior> ballastFlorae = new List<BallastFloraBehavior>();
|
||||
|
||||
@@ -698,7 +721,10 @@ namespace Barotrauma
|
||||
float branchDist = Vector2.Distance(branchWorldPos, worldPosition);
|
||||
if (branchDist < worldRange)
|
||||
{
|
||||
float distFactor = 1.0f - (branchDist / worldRange);
|
||||
float distFactor =
|
||||
distanceFalloff ?
|
||||
1.0f - (branchDist / worldRange) :
|
||||
1.0f;
|
||||
if (distFactor <= 0.0f) { return; }
|
||||
|
||||
Vector2 explosionPos = worldPosition;
|
||||
@@ -715,7 +741,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetObstacleDamageMultiplier(Vector2 explosionSimPos, Vector2 explosionWorldPos, Vector2 targetSimPos)
|
||||
private static float GetObstacleDamageMultiplier(Vector2 explosionSimPos, Vector2 explosionWorldPos, Vector2 targetSimPos, IEnumerable<Structure> ignoredCover = null)
|
||||
{
|
||||
float damageMultiplier = 1.0f;
|
||||
var obstacles = Submarine.PickBodies(targetSimPos, explosionSimPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
|
||||
@@ -728,6 +754,10 @@ namespace Barotrauma
|
||||
}
|
||||
else if (body.UserData is Structure structure)
|
||||
{
|
||||
if (ignoredCover != null)
|
||||
{
|
||||
if (ignoredCover.Contains(structure)) { continue; }
|
||||
}
|
||||
int sectionIndex = structure.FindSectionIndex(explosionWorldPos, world: true, clamp: true);
|
||||
if (structure.SectionBodyDisabled(sectionIndex))
|
||||
{
|
||||
|
||||
@@ -109,6 +109,8 @@ namespace Barotrauma
|
||||
|
||||
public float Size => IsHorizontal ? Rect.Height : Rect.Width;
|
||||
|
||||
public float PressureDistributionSpeed => Size / 100.0f * open;
|
||||
|
||||
private Door connectedDoor;
|
||||
public Door ConnectedDoor
|
||||
{
|
||||
@@ -427,11 +429,9 @@ namespace Barotrauma
|
||||
|
||||
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) { return; }
|
||||
|
||||
float size = IsHorizontal ? rect.Height : rect.Width;
|
||||
|
||||
//a variable affecting the water flow through the gap
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size / 100.0f * open;
|
||||
float sizeModifier = Size / 100.0f * open;
|
||||
|
||||
//horizontal gap (such as a regular door)
|
||||
if (IsHorizontal)
|
||||
@@ -440,7 +440,7 @@ namespace Barotrauma
|
||||
float delta = 0.0f;
|
||||
|
||||
//water level is above the lower boundary of the gap
|
||||
if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + subOffset.Y + hull2.WaveY[0]) > rect.Y - size)
|
||||
if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + subOffset.Y + hull2.WaveY[0]) > rect.Y - Size)
|
||||
{
|
||||
int dir = (hull1.Pressure > hull2.Pressure + subOffset.Y) ? 1 : -1;
|
||||
|
||||
@@ -569,27 +569,35 @@ namespace Barotrauma
|
||||
|
||||
if (open > 0.0f)
|
||||
{
|
||||
if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress && hull2.WaterVolume > hull2.Volume / Hull.MaxCompress)
|
||||
if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress &&
|
||||
hull2.WaterVolume > hull2.Volume / Hull.MaxCompress)
|
||||
{
|
||||
//both hulls full -> distribute pressure
|
||||
float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f;
|
||||
hull1.LethalPressure = avgLethality;
|
||||
hull2.LethalPressure = avgLethality;
|
||||
changePressure(hull1, avgLethality, PressureDistributionSpeed, deltaTime);
|
||||
changePressure(hull2, avgLethality, PressureDistributionSpeed, deltaTime);
|
||||
|
||||
static void changePressure(Hull hull, float target, float speed, float deltaTime)
|
||||
{
|
||||
float diff = target - hull.LethalPressure;
|
||||
float maxChange = Hull.PressureBuildUpSpeed * speed * deltaTime;
|
||||
hull.LethalPressure += MathHelper.Clamp(diff, -maxChange, maxChange);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hull1.LethalPressure -= Hull.PressureDropSpeed * deltaTime;
|
||||
hull2.LethalPressure -= Hull.PressureDropSpeed * deltaTime;
|
||||
//either hull not full -> pressure drops
|
||||
hull1.LethalPressure -= Hull.PressureDropSpeed * PressureDistributionSpeed * deltaTime;
|
||||
hull2.LethalPressure -= Hull.PressureDropSpeed * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateRoomToOut(float deltaTime, Hull hull1)
|
||||
{
|
||||
float size = IsHorizontal ? rect.Height : rect.Width;
|
||||
|
||||
//a variable affecting the water flow through the gap
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size * open * open;
|
||||
float sizeModifier = Size * open * open;
|
||||
|
||||
float delta = 500.0f * sizeModifier * deltaTime;
|
||||
|
||||
@@ -642,7 +650,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * deltaTime;
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -657,7 +665,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
|
||||
{
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * deltaTime;
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,7 +1016,11 @@ namespace Barotrauma
|
||||
|
||||
if (waterVolume < Volume)
|
||||
{
|
||||
LethalPressure -= PressureDropSpeed * deltaTime;
|
||||
//pressure drop speed is inversely proportionate to water percentage
|
||||
//= pressure drops very fast if the hull is nowhere near full
|
||||
float waterVolumeFactor = Math.Max((100.0f - WaterPercentage) / 10.0f, 1.0f);
|
||||
LethalPressure -=
|
||||
PressureDropSpeed * waterVolumeFactor * deltaTime;
|
||||
if (WaterVolume <= 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace Barotrauma
|
||||
if (FinishedEvents.Any())
|
||||
{
|
||||
var finishedEventsElement = new XElement(nameof(FinishedEvents));
|
||||
foreach (var (set, count) in FinishedEvents.DistinctBy(f => f.Key.Identifier))
|
||||
foreach (var (set, count) in FinishedEvents)
|
||||
{
|
||||
var element = new XElement(nameof(FinishedEvents),
|
||||
new XAttribute("set", set.Identifier),
|
||||
|
||||
@@ -1245,7 +1245,7 @@ namespace Barotrauma
|
||||
|
||||
private static void CreateWallDamageExplosion(Gap gap, Character attacker)
|
||||
{
|
||||
const float explosionRange = 750.0f;
|
||||
const float explosionRange = 500.0f;
|
||||
float explosionStrength = gap.Open;
|
||||
|
||||
var linkedHull = gap.linkedTo.FirstOrDefault() as Hull;
|
||||
@@ -1264,20 +1264,22 @@ namespace Barotrauma
|
||||
|
||||
if (explosionOnBroken == null)
|
||||
{
|
||||
explosionOnBroken = new Explosion(explosionRange, force: 10.0f, damage: 0.0f, structureDamage: 0.0f, itemDamage: 0.0f);
|
||||
explosionOnBroken = new Explosion(explosionRange, force: 5.0f, damage: 0.0f, structureDamage: 0.0f, itemDamage: 0.0f);
|
||||
if (AfflictionPrefab.Prefabs.TryGet("lacerations".ToIdentifier(), out AfflictionPrefab lacerations))
|
||||
{
|
||||
explosionOnBroken.Attack.Afflictions.Add(lacerations.Instantiate(3.0f), null);
|
||||
explosionOnBroken.Attack.Afflictions.Add(lacerations.Instantiate(5.0f), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
explosionOnBroken.Attack.Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(3.0f), null);
|
||||
explosionOnBroken.Attack.Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(5.0f), null);
|
||||
}
|
||||
explosionOnBroken.IgnoreCover = true;
|
||||
explosionOnBroken.IgnoreCover = false;
|
||||
explosionOnBroken.OnlyInside = true;
|
||||
explosionOnBroken.DistanceFalloff = false;
|
||||
explosionOnBroken.DisableParticles();
|
||||
}
|
||||
|
||||
explosionOnBroken.IgnoredCover = gap.ConnectedWall?.ToEnumerable();
|
||||
explosionOnBroken.Attack.Range = explosionRange * gap.Open;
|
||||
explosionOnBroken.Attack.DamageMultiplier = explosionStrength;
|
||||
explosionOnBroken.Attack.Stun = MathHelper.Clamp(explosionStrength, 0.5f, 1.0f);
|
||||
|
||||
@@ -1606,28 +1606,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removeItem)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
if (targets[i] is Item item) { Entity.Spawner?.AddItemToRemoveQueue(item); }
|
||||
}
|
||||
}
|
||||
if (removeCharacter)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (target is Character character)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(limb.character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (breakLimb || hideLimb)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
@@ -2292,6 +2271,30 @@ namespace Barotrauma
|
||||
|
||||
ApplyProjSpecific(deltaTime, entity, targets, hull, position, playSound: true);
|
||||
|
||||
//do this last - the entities spawned by the effect might need the entity for something, so better to remove it last
|
||||
if (removeItem)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
if (targets[i] is Item item) { Entity.Spawner?.AddItemToRemoveQueue(item); }
|
||||
}
|
||||
}
|
||||
if (removeCharacter)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (target is Character character)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(limb.character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oneShot)
|
||||
{
|
||||
Disabled = true;
|
||||
|
||||
Reference in New Issue
Block a user