v0.19.8.0
This commit is contained in:
@@ -466,7 +466,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run && Character.CanRun));
|
||||
|
||||
//if someone is grabbing the bot and the bot isn't trying to run anywhere, let them keep dragging and "control" the bot
|
||||
if (Character.SelectedBy == null || run)
|
||||
{
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run && Character.CanRun));
|
||||
}
|
||||
|
||||
bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));
|
||||
if (steeringManager == insideSteering)
|
||||
|
||||
+6
-3
@@ -913,11 +913,14 @@ namespace Barotrauma
|
||||
|
||||
private void RemoveFollowTarget()
|
||||
{
|
||||
if (arrestingRegistered)
|
||||
if (followTargetObjective != null)
|
||||
{
|
||||
followTargetObjective.Completed -= OnArrestTargetReached;
|
||||
if (arrestingRegistered)
|
||||
{
|
||||
followTargetObjective.Completed -= OnArrestTargetReached;
|
||||
}
|
||||
RemoveSubObjective(ref followTargetObjective);
|
||||
}
|
||||
RemoveSubObjective(ref followTargetObjective);
|
||||
arrestingRegistered = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -595,6 +595,10 @@ namespace Barotrauma
|
||||
{
|
||||
return c.CurrentHull;
|
||||
}
|
||||
else if (target is Structure structure)
|
||||
{
|
||||
return Hull.FindHull(structure.Position, useWorldCoordinates: false);
|
||||
}
|
||||
else if (target is Gap g)
|
||||
{
|
||||
return g.FlowTargetHull;
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
if (ValidContainableItemIdentifiers.None())
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError($"No valid containable item identifiers found for the Load Item objective targeting {Container}");
|
||||
DebugConsole.LogError($"No valid containable item identifiers found for the Load Item objective targeting {Container}");
|
||||
#endif
|
||||
Abandon = true;
|
||||
return;
|
||||
@@ -250,7 +250,7 @@ namespace Barotrauma
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
|
||||
DebugConsole.LogError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
|
||||
#endif
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ namespace Barotrauma
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
|
||||
DebugConsole.LogError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
+17
-4
@@ -37,6 +37,8 @@ namespace Barotrauma
|
||||
public Func<bool> completionCondition;
|
||||
private bool isDoneOperating;
|
||||
|
||||
public float? OverridePriority = null;
|
||||
|
||||
protected override float GetPriority()
|
||||
{
|
||||
bool isOrder = objectiveManager.IsOrder(this);
|
||||
@@ -52,7 +54,11 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isOrder)
|
||||
if (OverridePriority.HasValue)
|
||||
{
|
||||
Priority = OverridePriority.Value;
|
||||
}
|
||||
else if (isOrder)
|
||||
{
|
||||
Priority = objectiveManager.GetOrderPriority(this);
|
||||
}
|
||||
@@ -135,7 +141,7 @@ namespace Barotrauma
|
||||
float value = CumulatedDevotion + (max * PriorityModifier);
|
||||
Priority = MathHelper.Clamp(value, 0, max);
|
||||
}
|
||||
else
|
||||
else if (!OverridePriority.HasValue)
|
||||
{
|
||||
float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier);
|
||||
float max = AIObjectiveManager.LowestOrderPriority - 1;
|
||||
@@ -204,8 +210,15 @@ namespace Barotrauma
|
||||
{
|
||||
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
|
||||
{
|
||||
HumanAIController.FaceTarget(target.Item);
|
||||
if (character.SelectedItem != target.Item)
|
||||
if (target.Item.GetComponent<Controller>() is not Controller { ControlCharacterPose: true })
|
||||
{
|
||||
HumanAIController.FaceTarget(target.Item);
|
||||
}
|
||||
else
|
||||
{
|
||||
HumanAIController.SteeringManager.Reset();
|
||||
}
|
||||
if (character.SelectedItem != target.Item && character.SelectedSecondaryItem != target.Item)
|
||||
{
|
||||
target.Item.TryInteract(character, forceSelectKey: true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -278,7 +278,7 @@ namespace Barotrauma
|
||||
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
|
||||
|
||||
//find which treatments are the most suitable to treat the character's current condition
|
||||
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false, predictFutureDuration: 10.0f);
|
||||
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, user: character, normalize: false, predictFutureDuration: 10.0f);
|
||||
|
||||
//check if we already have a suitable treatment for any of the afflictions
|
||||
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))
|
||||
|
||||
@@ -3,9 +3,8 @@ using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -151,7 +150,7 @@ namespace Barotrauma
|
||||
public OrderPrefab(ContentXElement orderElement, OrdersFile file) : base(file, orderElement.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
Name = TextManager.Get($"OrderName.{Identifier}");
|
||||
ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}");
|
||||
ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}").Fallback(Name);
|
||||
|
||||
string targetItemType = orderElement.GetAttributeString("targetitemtype", "");
|
||||
if (!string.IsNullOrWhiteSpace(targetItemType))
|
||||
@@ -435,7 +434,7 @@ namespace Barotrauma
|
||||
}
|
||||
catch (NotImplementedException e)
|
||||
{
|
||||
DebugConsole.ShowError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}");
|
||||
DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -336,7 +336,7 @@ namespace Barotrauma
|
||||
{
|
||||
ApplyTestPose();
|
||||
}
|
||||
else
|
||||
else if (character.SelectedBy == null)
|
||||
{
|
||||
if (character.LockHands)
|
||||
{
|
||||
@@ -356,7 +356,7 @@ namespace Barotrauma
|
||||
HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
}
|
||||
if (Anim != Animation.UsingItem && character.SelectedBy == null)
|
||||
if (Anim != Animation.UsingItem)
|
||||
{
|
||||
if (Anim != Animation.UsingItemWhileClimbing)
|
||||
{
|
||||
@@ -1716,9 +1716,17 @@ namespace Barotrauma
|
||||
}
|
||||
else if (target is AICharacter && target != Character.Controlled)
|
||||
{
|
||||
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left;
|
||||
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Dir) - target.SimPosition;
|
||||
target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero;
|
||||
if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
|
||||
{
|
||||
target.AnimController.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left;
|
||||
}
|
||||
//make the target stand 0.5 meters away from this character, on the side they're currently at
|
||||
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Math.Sign(target.SimPosition.X - character.SimPosition.X)) - target.SimPosition;
|
||||
target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,9 +402,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
|
||||
if (afflictionPrefab == null)
|
||||
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out afflictionPrefab))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
continue;
|
||||
@@ -430,15 +429,13 @@ namespace Barotrauma
|
||||
Afflictions.Clear();
|
||||
foreach (var subElement in element.GetChildElements("affliction"))
|
||||
{
|
||||
AfflictionPrefab afflictionPrefab;
|
||||
Affliction affliction;
|
||||
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
|
||||
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier))
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab afflictionPrefab))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\".");
|
||||
continue;
|
||||
}
|
||||
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
|
||||
affliction = afflictionPrefab.Instantiate(0.0f);
|
||||
affliction.Deserialize(subElement);
|
||||
//backwards compatibility
|
||||
|
||||
@@ -549,6 +549,10 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f);
|
||||
if (value)
|
||||
{
|
||||
SelectedCharacter = null;
|
||||
}
|
||||
#if CLIENT
|
||||
HintManager.OnHandcuffed(this);
|
||||
#endif
|
||||
@@ -599,13 +603,10 @@ namespace Barotrauma
|
||||
get { return selectedCharacter; }
|
||||
set
|
||||
{
|
||||
if (value == selectedCharacter) return;
|
||||
if (selectedCharacter != null)
|
||||
selectedCharacter.selectedBy = null;
|
||||
if (value == selectedCharacter) { return; }
|
||||
if (selectedCharacter != null) { selectedCharacter.selectedBy = null; }
|
||||
selectedCharacter = value;
|
||||
if (selectedCharacter != null)
|
||||
selectedCharacter.selectedBy = this;
|
||||
|
||||
if (selectedCharacter != null) {selectedCharacter.selectedBy = this; }
|
||||
#if CLIENT
|
||||
CharacterHealth.SetHealthBarVisibility(value == null);
|
||||
#endif
|
||||
@@ -707,6 +708,14 @@ namespace Barotrauma
|
||||
get { return CurrentHull == null || CurrentHull.LethalPressure > 5.0f; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects
|
||||
/// </summary>
|
||||
public AnimController.Animation Anim
|
||||
{
|
||||
get { return AnimController?.Anim ?? AnimController.Animation.None; }
|
||||
}
|
||||
|
||||
public const float KnockbackCooldown = 5.0f;
|
||||
public float KnockbackCooldownTimer;
|
||||
|
||||
@@ -2620,7 +2629,7 @@ namespace Barotrauma
|
||||
if (!AllowInput)
|
||||
{
|
||||
FocusedCharacter = null;
|
||||
if (SelectedCharacter != null) DeselectCharacter();
|
||||
if (SelectedCharacter != null) { DeselectCharacter(); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3636,7 +3645,7 @@ namespace Barotrauma
|
||||
string modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Message, message.MessageType.Value, this, Controlled);
|
||||
if (!string.IsNullOrEmpty(modifiedMessage))
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(info.Name, modifiedMessage, message.MessageType.Value, this);
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(Name, modifiedMessage, message.MessageType.Value, this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4020,6 +4029,7 @@ namespace Barotrauma
|
||||
if (newStun > 0.0f)
|
||||
{
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
if (SelectedCharacter != null) { DeselectCharacter(); }
|
||||
}
|
||||
HealthUpdateInterval = 0.0f;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -25,9 +26,10 @@ namespace Barotrauma
|
||||
UpdateSkills = 12,
|
||||
UpdateMoney = 13,
|
||||
UpdatePermanentStats = 14,
|
||||
RemoveFromCrew = 15,
|
||||
|
||||
MinValue = 0,
|
||||
MaxValue = 14
|
||||
MaxValue = 15
|
||||
}
|
||||
|
||||
private interface IEventData : NetEntityEvent.IData
|
||||
@@ -133,18 +135,30 @@ namespace Barotrauma
|
||||
public EventType EventType => EventType.TeamChange;
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
public readonly record struct ItemTeamChange(CharacterTeamType TeamId, ImmutableArray<UInt16> ItemIds) : INetSerializableStruct;
|
||||
|
||||
|
||||
public struct AddToCrewEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.AddToCrew;
|
||||
public readonly CharacterTeamType TeamType;
|
||||
public readonly ImmutableArray<Item> InventoryItems;
|
||||
public readonly ItemTeamChange ItemTeamChange;
|
||||
|
||||
public AddToCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
|
||||
{
|
||||
TeamType = teamType;
|
||||
InventoryItems = inventoryItems.ToImmutableArray();
|
||||
ItemTeamChange = new ItemTeamChange(teamType, inventoryItems.Select(it => it.ID).ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
public struct RemoveFromCrewEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.RemoveFromCrew;
|
||||
public readonly ItemTeamChange ItemTeamChange;
|
||||
|
||||
public RemoveFromCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
|
||||
{
|
||||
ItemTeamChange = new ItemTeamChange(teamType, inventoryItems.Select(it => it.ID).ToImmutableArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct UpdateExperienceEventData : IEventData
|
||||
|
||||
@@ -82,10 +82,10 @@ namespace Barotrauma
|
||||
public UInt16 networkUpdateID;
|
||||
}
|
||||
|
||||
private List<NetInputMem> memInput = new List<NetInputMem>();
|
||||
private readonly List<NetInputMem> memInput = new List<NetInputMem>();
|
||||
|
||||
private List<CharacterStateInfo> memState = new List<CharacterStateInfo>();
|
||||
private List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
|
||||
private readonly List<CharacterStateInfo> memState = new List<CharacterStateInfo>();
|
||||
private readonly List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
|
||||
|
||||
public float healthUpdateTimer;
|
||||
|
||||
|
||||
@@ -1038,7 +1038,7 @@ namespace Barotrauma
|
||||
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
|
||||
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
|
||||
/// <param name="predictFutureDuration">If above 0, the method will take into account how much currently active status effects while affect the afflictions in the next x seconds.</param>
|
||||
public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float predictFutureDuration = 0.0f)
|
||||
public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, bool normalize, Character user, Limb limb = null, bool ignoreHiddenAfflictions = false, float predictFutureDuration = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
@@ -1062,7 +1062,18 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (strength <= affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (ignoreHiddenAfflictions && strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
|
||||
if (ignoreHiddenAfflictions)
|
||||
{
|
||||
if (user == Character)
|
||||
{
|
||||
if (strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strength < affliction.Prefab.ShowIconToOthersThreshold) { continue; }
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<Identifier, float> treatment in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
@@ -1153,10 +1164,10 @@ namespace Barotrauma
|
||||
msg.WriteRangedSingle(
|
||||
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
|
||||
0.0f, affliction.Prefab.MaxStrength, 8);
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count);
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
|
||||
{
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], 0, periodicEffect.MaxInterval, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,7 +1189,7 @@ namespace Barotrauma
|
||||
msg.WriteRangedSingle(
|
||||
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
|
||||
0.0f, affliction.Prefab.MaxStrength, 8);
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count);
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
|
||||
{
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
|
||||
@@ -86,6 +86,28 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names.");
|
||||
}
|
||||
foreach (var afflictionType in parsedAfflictionTypes)
|
||||
{
|
||||
if (!AfflictionPrefab.Prefabs.Any(p => p.AfflictionType == afflictionType))
|
||||
{
|
||||
createWarningOrError($"Potentially invalid damage modifier in \"{parentDebugName}\". Could not find any afflictions of the type \"{afflictionType}\". Did you mean to use an affliction identifier instead?");
|
||||
}
|
||||
}
|
||||
foreach (var afflictionIdentifier in parsedAfflictionIdentifiers)
|
||||
{
|
||||
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier))
|
||||
{
|
||||
createWarningOrError($"Potentially invalid damage modifier in \"{parentDebugName}\". Could not find any afflictions with the identifier \"{afflictionIdentifier}\". Did you mean to use an affliction type instead?");
|
||||
}
|
||||
}
|
||||
static void createWarningOrError(string msg)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(msg);
|
||||
#else
|
||||
DebugConsole.AddWarning(msg);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseAfflictionTypes()
|
||||
|
||||
+2
-6
@@ -1,8 +1,4 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
@@ -31,7 +27,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
if (!SelectedItemHasTag(closestCharacter)) { return; }
|
||||
if (closestCharacter == null || !SelectedItemHasTag(closestCharacter)) { return; }
|
||||
|
||||
if (closestDistance < squaredMaxDistance)
|
||||
{
|
||||
|
||||
@@ -1267,7 +1267,11 @@ namespace Barotrauma
|
||||
}
|
||||
}, () =>
|
||||
{
|
||||
return new[] { FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray() };
|
||||
return new[]
|
||||
{
|
||||
FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray(),
|
||||
GameMain.GameSession?.Campaign.Factions.Select(f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
|
||||
};
|
||||
}, true));
|
||||
|
||||
commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) =>
|
||||
@@ -2235,7 +2239,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowError(string msg, Color? color = null)
|
||||
public static void LogError(string msg, Color? color = null)
|
||||
{
|
||||
color ??= Color.Red;
|
||||
NewMessage(msg, color.Value, isCommand: false, isError: true);
|
||||
@@ -2407,7 +2411,7 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
ShowError(error);
|
||||
LogError(error);
|
||||
}
|
||||
|
||||
public static void AddWarning(string warning)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
+6
-4
@@ -38,10 +38,12 @@ namespace Barotrauma
|
||||
}
|
||||
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
|
||||
{
|
||||
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
|
||||
if (limbType == null) { return false; }
|
||||
|
||||
return limbType == TargetLimb && affliction.Strength >= MinStrength;
|
||||
if (affliction.Prefab.LimbSpecific)
|
||||
{
|
||||
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
|
||||
if (limbType == null || limbType != TargetLimb) { return false; }
|
||||
}
|
||||
return affliction.Strength >= MinStrength;
|
||||
});
|
||||
|
||||
if (afflictions.Any(a => a.Identifier == Identifier)) { return true; }
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.");
|
||||
}
|
||||
foreach (var attribute in element.Attributes())
|
||||
{
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (Conditional == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
|
||||
}
|
||||
|
||||
static bool IsTargetTagAttribute(XAttribute attribute) => attribute.NameAsIdentifier() == "targettag";
|
||||
@@ -52,7 +52,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
|
||||
}
|
||||
if (target == null || Conditional == null)
|
||||
{
|
||||
|
||||
+10
-16
@@ -18,10 +18,14 @@ class CheckConnectionAction : BinaryOptionAction
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OtherConnectionName { get; set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.Yes)]
|
||||
public int MinAmount { get; set; }
|
||||
|
||||
public CheckConnectionAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
int amount = 0;
|
||||
var connectTargets = !ConnectedItemTag.IsEmpty ? ParentEvent.GetTargets(ConnectedItemTag) : Enumerable.Empty<Entity>();
|
||||
foreach (var target in ParentEvent.GetTargets(ItemTag))
|
||||
{
|
||||
@@ -33,27 +37,17 @@ class CheckConnectionAction : BinaryOptionAction
|
||||
if (!IsCorrectConnection(connection, ConnectionName)) { continue; }
|
||||
if (ConnectedItemTag.IsEmpty && OtherConnectionName.IsEmpty)
|
||||
{
|
||||
if (connection.Wires.Any()) { return true; }
|
||||
amount += connection.Wires.Count();
|
||||
if (amount >= MinAmount) { return true; }
|
||||
continue;
|
||||
}
|
||||
foreach (var wire in connection.Wires)
|
||||
{
|
||||
if (wire.OtherConnection(connection) is not Connection otherConnection) { continue; }
|
||||
if (ConnectedItemTag.IsEmpty)
|
||||
{
|
||||
if (IsCorrectConnection(otherConnection, OtherConnectionName)) { return true; }
|
||||
}
|
||||
else if (OtherConnectionName.IsEmpty)
|
||||
{
|
||||
if (IsCorrectItem()) { return true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
|
||||
if (!IsCorrectItem()) { continue; }
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ConnectedItemTag.IsEmpty && !IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
|
||||
if (!ConnectedItemTag.IsEmpty && !IsCorrectItem()) { continue; }
|
||||
amount++;
|
||||
if (amount >= MinAmount) { return true; }
|
||||
bool IsCorrectItem() => connectTargets.Contains(otherConnection.Item);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -36,15 +35,32 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public CheckDataAction(ContentXElement element, string parentDebugString) : base(null, element)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
Condition = element.GetAttributeString("value", string.Empty)!;
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetSuccess()
|
||||
{
|
||||
return DetermineSuccess() ?? false;
|
||||
}
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
if (!(GameMain.GameSession?.GameMode is CampaignMode campaignMode)) { return false; }
|
||||
if (GameMain.GameSession?.GameMode is not CampaignMode campaignMode) { return false; }
|
||||
|
||||
string[] splitString = Condition.Split(' ');
|
||||
string value = Condition;
|
||||
string value;
|
||||
if (splitString.Length > 0)
|
||||
{
|
||||
#warning Is this correct?
|
||||
//the first part of the string is the operator, skip it
|
||||
value = string.Join(" ", splitString.Skip(1));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -15,8 +16,19 @@ namespace Barotrauma
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string ItemTags { get; set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.Yes)]
|
||||
public int Amount { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Tag to apply to the first target when the check succeeds.")]
|
||||
public Identifier ApplyTagToTarget { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool RequireEquipped { get; set; }
|
||||
|
||||
[Serialize(-1, IsPropertySaveable.Yes)]
|
||||
public int ItemContainerIndex { get; set; }
|
||||
|
||||
private readonly IReadOnlyList<PropertyConditional> conditionals;
|
||||
|
||||
private readonly Identifier[] itemIdentifierSplit;
|
||||
private readonly Identifier[] itemTags;
|
||||
@@ -25,6 +37,19 @@ namespace Barotrauma
|
||||
{
|
||||
itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers();
|
||||
itemTags = ItemTags.Split(",").ToIdentifiers();
|
||||
var conditionalList = new List<PropertyConditional>();
|
||||
foreach (ContentXElement subElement in element.GetChildElements("conditional"))
|
||||
{
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
if (PropertyConditional.IsValid(attribute))
|
||||
{
|
||||
conditionalList.Add(new PropertyConditional(attribute));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
conditionals = conditionalList;
|
||||
}
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
@@ -35,25 +60,70 @@ namespace Barotrauma
|
||||
{
|
||||
if (target is Character character)
|
||||
{
|
||||
if (RequireEquipped)
|
||||
Inventory inventory = character.Inventory;
|
||||
if (CheckInventory(character.Inventory, character))
|
||||
{
|
||||
if (itemTags.Any(tag => character.HasEquippedItem(tag))) { return true; }
|
||||
if (itemIdentifierSplit.Any(identifier => character.HasEquippedItem(identifier))) { return true; }
|
||||
return false;
|
||||
if (!ApplyTagToTarget.IsEmpty)
|
||||
{
|
||||
ParentEvent.AddTarget(ApplyTagToTarget, target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (character.Inventory is not CharacterInventory inventory) { continue; }
|
||||
if (itemTags.Any(tag => inventory.FindItemByTag(tag, recursive: true) is not null)) { return true; }
|
||||
if (itemIdentifierSplit.Any(identifier => inventory.FindItemByIdentifier(identifier, recursive: true) is not null)) { return true; }
|
||||
}
|
||||
else if (target is Item item && item.OwnInventory is ItemInventory inventory)
|
||||
else if (target is Item item)
|
||||
{
|
||||
if (itemTags.Any(tag => inventory.FindItemByTag(tag, recursive: true) is not null)) { return true; }
|
||||
if (itemIdentifierSplit.Any(identifier => inventory.FindItemByIdentifier(identifier, recursive: true) is not null)) { return true; }
|
||||
int i = 0;
|
||||
foreach (var itemContainer in item.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (ItemContainerIndex == -1 || i == ItemContainerIndex)
|
||||
{
|
||||
if (CheckInventory(itemContainer.Inventory, character: null))
|
||||
{
|
||||
if (!ApplyTagToTarget.IsEmpty)
|
||||
{
|
||||
ParentEvent.AddTarget(ApplyTagToTarget, target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckInventory(Inventory inventory, Character character)
|
||||
{
|
||||
if (inventory == null) { return false; }
|
||||
int count = 0;
|
||||
foreach (Item item in inventory.FindAllItems(it => itemTags.Any(it.HasTag) || itemIdentifierSplit.Contains(it.Prefab.Identifier)))
|
||||
{
|
||||
if (!ConditionalsMatch(item, character)) { continue; }
|
||||
count++;
|
||||
if (count >= Amount) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ConditionalsMatch(Item item, Character character = null)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
foreach (PropertyConditional conditional in conditionals)
|
||||
{
|
||||
if (!conditional.Matches(item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (RequireEquipped)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
return character.HasEquippedItem(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Barotrauma
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderOption { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderTargetTag { get; set; }
|
||||
|
||||
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
@@ -29,20 +32,17 @@ namespace Barotrauma
|
||||
}
|
||||
if (targetCharacter == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target character was found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target character was found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
return false;
|
||||
}
|
||||
var currentOrderInfo = targetCharacter.GetCurrentOrderWithTopPriority();
|
||||
if (currentOrderInfo?.Identifier == OrderIdentifier)
|
||||
{
|
||||
if (OrderOption.IsEmpty)
|
||||
if (!OrderTargetTag.IsEmpty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentOrderInfo?.Option == OrderOption;
|
||||
if (currentOrderInfo.TargetEntity is not Item targetItem || !targetItem.HasTag(OrderTargetTag)) { return false; }
|
||||
}
|
||||
return OrderOption.IsEmpty || currentOrderInfo?.Option == OrderOption;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CheckSelectedItemAction : BinaryOptionAction
|
||||
{
|
||||
public enum SelectedItemType { Primary, Secondary, Any };
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier CharacterTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)]
|
||||
public SelectedItemType ItemType { get; set; }
|
||||
|
||||
public CheckSelectedItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
Character character = null;
|
||||
if (!CharacterTag.IsEmpty)
|
||||
{
|
||||
foreach (var t in ParentEvent.GetTargets(CharacterTag))
|
||||
{
|
||||
if (t is Character c)
|
||||
{
|
||||
character = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (character == null)
|
||||
{
|
||||
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail.");
|
||||
return false;
|
||||
}
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag);
|
||||
if (targets.None())
|
||||
{
|
||||
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
return false;
|
||||
}
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target is not Item targetItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IsSelected(targetItem))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
bool IsSelected(Item item)
|
||||
{
|
||||
return ItemType switch
|
||||
{
|
||||
SelectedItemType.Any => character.IsAnySelectedItem(item),
|
||||
SelectedItemType.Primary => character.SelectedItem == item,
|
||||
SelectedItemType.Secondary => character.SelectedSecondaryItem == item,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemType switch
|
||||
{
|
||||
SelectedItemType.Any => !character.HasSelectedAnyItem,
|
||||
SelectedItemType.Primary => character.SelectedItem == null,
|
||||
SelectedItemType.Secondary => character.SelectedSecondaryItem == null,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private string GetEventName()
|
||||
{
|
||||
return ParentEvent?.Prefab?.Identifier is { IsEmpty: false } identifier ? $"the event \"{identifier}\"" : "an unknown event";
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CheckSelectedItemAction : BinaryOptionAction
|
||||
class CheckSelectedAction : BinaryOptionAction
|
||||
{
|
||||
public enum SelectedItemType { Primary, Secondary, Any };
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Barotrauma
|
||||
[Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)]
|
||||
public SelectedItemType ItemType { get; set; }
|
||||
|
||||
public CheckSelectedItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
public CheckSelectedAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (character == null)
|
||||
{
|
||||
DebugConsole.ShowError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail.");
|
||||
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail.");
|
||||
return false;
|
||||
}
|
||||
if (!TargetTag.IsEmpty)
|
||||
@@ -42,11 +42,16 @@ namespace Barotrauma
|
||||
IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag);
|
||||
if (targets.None())
|
||||
{
|
||||
DebugConsole.ShowError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
return false;
|
||||
}
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target is Character targetCharacter)
|
||||
{
|
||||
if (ItemType == SelectedItemType.Any && character.SelectedCharacter == targetCharacter) { return true; }
|
||||
continue;
|
||||
}
|
||||
if (target is not Item targetItem)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Barotrauma
|
||||
|
||||
public static EventAction Instantiate(ScriptedEvent scriptedEvent, ContentXElement element)
|
||||
{
|
||||
Type actionType = null;
|
||||
Type actionType;
|
||||
try
|
||||
{
|
||||
actionType = Type.GetType("Barotrauma." + element.Name, true, true);
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class GodModeAction : EventAction
|
||||
@@ -10,6 +5,9 @@ namespace Barotrauma
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the character's active afflictions be updated (e.g. applying visual effects of the afflictions)")]
|
||||
public bool UpdateAfflictions { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
@@ -35,9 +33,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (target != null && target is Character character)
|
||||
{
|
||||
character.GodMode = Enabled;
|
||||
if (UpdateAfflictions)
|
||||
{
|
||||
character.CharacterHealth.Unkillable = Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.GodMode = Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class InventoryHighlightAction : EventAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier ItemIdentifier { get; set; }
|
||||
|
||||
[Serialize(-1, IsPropertySaveable.Yes)]
|
||||
public int ItemContainerIndex { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool Recursive { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public InventoryHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
UpdateProjSpecific();
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific();
|
||||
|
||||
public override bool IsFinished(ref string goToLabel) => isFinished;
|
||||
|
||||
public override void Reset() => isFinished = false;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ namespace Barotrauma
|
||||
{
|
||||
partial class MessageBoxAction : EventAction
|
||||
{
|
||||
public enum ActionType { Create, Close }
|
||||
public enum ActionType { Create, ConnectObjective, Close, Clear }
|
||||
|
||||
[Serialize(ActionType.Create, IsPropertySaveable.Yes)]
|
||||
public ActionType Type { get; set; }
|
||||
@@ -51,7 +51,13 @@ namespace Barotrauma
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public MessageBoxAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
public MessageBoxAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
if (Identifier.IsEmpty)
|
||||
{
|
||||
Identifier = element.GetAttributeIdentifier("id", Identifier.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.GameSession.GameMode is CampaignMode campaign)
|
||||
{
|
||||
MissionPrefab prefab = null;
|
||||
Mission unlockedMission = null;
|
||||
var unlockLocation = FindUnlockLocation();
|
||||
if (unlockLocation == null && CreateLocationIfNotFound)
|
||||
{
|
||||
@@ -72,27 +72,34 @@ namespace Barotrauma
|
||||
{
|
||||
if (!MissionIdentifier.IsEmpty)
|
||||
{
|
||||
prefab = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
}
|
||||
else if (!MissionTag.IsEmpty)
|
||||
{
|
||||
prefab = unlockLocation.UnlockMissionByTag(MissionTag);
|
||||
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag);
|
||||
}
|
||||
if (campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
|
||||
}
|
||||
if (prefab != null)
|
||||
if (unlockedMission != null)
|
||||
{
|
||||
DebugConsole.NewMessage($"Unlocked mission \"{prefab.Name}\" in the location \"{unlockLocation.Name}\".");
|
||||
#if CLIENT
|
||||
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
|
||||
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
|
||||
if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] ==null)
|
||||
{
|
||||
IconColor = prefab.IconColor
|
||||
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.Name}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the connection from \"{unlockedMission.Locations[0].Name}\" to \"{unlockedMission.Locations[1].Name}\".");
|
||||
}
|
||||
#if CLIENT
|
||||
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", unlockedMission.Name),
|
||||
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: unlockedMission.Prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
|
||||
{
|
||||
IconColor = unlockedMission.Prefab.IconColor
|
||||
};
|
||||
#else
|
||||
NotifyMissionUnlock(prefab);
|
||||
#else
|
||||
NotifyMissionUnlock(unlockedMission);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -138,16 +145,17 @@ namespace Barotrauma
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MissionAction)} -> ({(MissionIdentifier.IsEmpty ? MissionTag : MissionIdentifier)})";
|
||||
}
|
||||
|
||||
|
||||
#if SERVER
|
||||
private void NotifyMissionUnlock(MissionPrefab prefab)
|
||||
private void NotifyMissionUnlock(Mission mission)
|
||||
{
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION);
|
||||
outmsg.WriteIdentifier(prefab.Identifier);
|
||||
outmsg.WriteIdentifier(mission.Prefab.Identifier);
|
||||
outmsg.WriteString(mission.Name.Value);
|
||||
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
+29
-13
@@ -16,6 +16,9 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool AddToCrew { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool RemoveFromCrew { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public NPCChangeTeamAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
@@ -35,34 +38,47 @@ namespace Barotrauma
|
||||
if (AddToCrew && (TeamTag == CharacterTeamType.Team1 || TeamTag == CharacterTeamType.Team2))
|
||||
{
|
||||
npc.Info.StartItemsGiven = true;
|
||||
|
||||
GameMain.GameSession.CrewManager.AddCharacter(npc);
|
||||
ChangeItemTeam(Submarine.MainSub, true);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamTag, npc.Inventory.AllItems));
|
||||
}
|
||||
}
|
||||
else if (RemoveFromCrew && (npc.TeamID == CharacterTeamType.Team1 || npc.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
npc.Info.StartItemsGiven = true;
|
||||
GameMain.GameSession.CrewManager.RemoveCharacter(npc, removeInfo: true);
|
||||
var sub = Submarine.Loaded.FirstOrDefault(s => s.TeamID == TeamTag);
|
||||
ChangeItemTeam(sub, false);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.RemoveFromCrewEventData(TeamTag, npc.Inventory.AllItems));
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeItemTeam(Submarine sub, bool allowStealing)
|
||||
{
|
||||
foreach (Item item in npc.Inventory.AllItems)
|
||||
{
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
item.AllowStealing = allowStealing;
|
||||
if (item.GetComponent<Items.Components.WifiComponent>() is { } wifiComponent)
|
||||
{
|
||||
wifiComponent.TeamID = TeamTag;
|
||||
}
|
||||
var idCard = item.GetComponent<Items.Components.IdCard>();
|
||||
if (idCard != null)
|
||||
if (item.GetComponent<Items.Components.IdCard>() is { } idCard)
|
||||
{
|
||||
idCard.TeamID = TeamTag;
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
WayPoint subWaypoint =
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human);
|
||||
WayPoint subWaypoint =
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human);
|
||||
if (subWaypoint != null)
|
||||
{
|
||||
npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true);
|
||||
}
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamTag, npc.Inventory.AllItems));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -17,6 +14,12 @@ namespace Barotrauma
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool Follow { get; set; }
|
||||
|
||||
[Serialize(-1, IsPropertySaveable.Yes)]
|
||||
public int MaxTargets { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AbandonOnReset { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public NPCFollowAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
@@ -32,6 +35,7 @@ namespace Barotrauma
|
||||
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault();
|
||||
if (target == null) { return; }
|
||||
|
||||
int targetCount = 0;
|
||||
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
@@ -56,6 +60,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
targetCount++;
|
||||
if (MaxTargets > -1 && targetCount >= MaxTargets)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
@@ -67,11 +76,11 @@ namespace Barotrauma
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
if (affectedNpcs != null && target != null)
|
||||
if (affectedNpcs != null && target != null && AbandonOnReset)
|
||||
{
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
if (npc.Removed || !(npc.AIController is HumanAIController humanAiController)) { continue; }
|
||||
if (npc.Removed || npc.AIController is not HumanAIController humanAiController) { continue; }
|
||||
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
|
||||
{
|
||||
if (goToObjective.Target == target)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class NPCOperateItemAction : EventAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier NPCTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier ItemComponentName { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderOption { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool RequireEquip { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool Operate { get; set; }
|
||||
|
||||
[Serialize(-1, IsPropertySaveable.Yes)]
|
||||
public int MaxTargets { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AbandonOnReset { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public NPCOperateItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
|
||||
private List<Character> affectedNpcs = null;
|
||||
private Item target = null;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
|
||||
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault() as Item;
|
||||
if (target == null) { return; }
|
||||
|
||||
int targetCount = 0;
|
||||
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
if (npc.AIController is not HumanAIController humanAiController) { continue; }
|
||||
|
||||
if (Operate)
|
||||
{
|
||||
ItemComponentName = "Controller".ToIdentifier();
|
||||
var itemComponent = target.Components.FirstOrDefault(ic => ItemComponentName == ic.Name);
|
||||
if (itemComponent == null)
|
||||
{
|
||||
DebugConsole.AddWarning($"Error in NPCOperateItemAction: could not find the component \"{ItemComponentName}\" in item \"{target.Name}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
var newObjective = new AIObjectiveOperateItem(itemComponent, npc, humanAiController.ObjectiveManager, OrderOption, RequireEquip)
|
||||
{
|
||||
OverridePriority = 100.0f
|
||||
};
|
||||
humanAiController.ObjectiveManager.AddObjective(newObjective);
|
||||
humanAiController.ObjectiveManager.WaitTimer = 0.0f;
|
||||
humanAiController.ObjectiveManager.Objectives.RemoveAll(o => o is AIObjectiveGoTo gotoOjective);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var objective in humanAiController.ObjectiveManager.Objectives)
|
||||
{
|
||||
if (objective is AIObjectiveOperateItem operateItemObjective && operateItemObjective.OperateTarget == target)
|
||||
{
|
||||
objective.Abandon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
targetCount++;
|
||||
if (MaxTargets > -1 && targetCount >= MaxTargets)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
if (affectedNpcs != null && target != null && AbandonOnReset)
|
||||
{
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
if (npc.Removed || npc.AIController is not HumanAIController humanAiController) { continue; }
|
||||
foreach (var operateItemObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveOperateItem>())
|
||||
{
|
||||
if (operateItemObjective.OperateTarget == target)
|
||||
{
|
||||
operateItemObjective.Abandon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = null;
|
||||
affectedNpcs = null;
|
||||
}
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(AIObjectiveOperateItem)} -> (NPCTag: {NPCTag.ColorizeObject()}, TargetTag: {TargetTag.ColorizeObject()}, Operate: {Operate.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
if (!(npc.AIController is HumanAIController humanAiController)) { continue; }
|
||||
if (npc.AIController is not HumanAIController humanAiController) { continue; }
|
||||
|
||||
if (Wait)
|
||||
{
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
if (npc.Removed || !(npc.AIController is HumanAIController)) { continue; }
|
||||
if (npc.Removed || npc.AIController is not HumanAIController) { continue; }
|
||||
if (gotoObjective != null)
|
||||
{
|
||||
gotoObjective.Abandon = true;
|
||||
|
||||
@@ -60,6 +60,9 @@ namespace Barotrauma
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "If false, we won't spawn another character if one with the same identifier has already been spawned.")]
|
||||
public bool AllowDuplicates { get; set; }
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes)]
|
||||
public float Offset { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, "What outpost module tags does the entity prefer to spawn in.")]
|
||||
public string TargetModuleTags
|
||||
{
|
||||
@@ -127,7 +130,7 @@ namespace Barotrauma
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), humanPrefab.CreateCharacterInfo(), onSpawn: newCharacter =>
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, Offset), humanPrefab.CreateCharacterInfo(), onSpawn: newCharacter =>
|
||||
{
|
||||
if (newCharacter == null) { return; }
|
||||
newCharacter.HumanPrefab = humanPrefab;
|
||||
@@ -162,7 +165,7 @@ namespace Barotrauma
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(SpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), onSpawn: newCharacter =>
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(SpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, Offset), onSpawn: newCharacter =>
|
||||
{
|
||||
if (!TargetTag.IsEmpty && newCharacter != null)
|
||||
{
|
||||
@@ -208,7 +211,7 @@ namespace Barotrauma
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), onSpawned: onSpawned);
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, Offset), onSpawned: onSpawned);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -13,7 +13,7 @@ class TutorialIconAction : EventAction
|
||||
public Identifier TargetTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string IconStyle { get; set; }
|
||||
public Identifier IconStyle { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
@@ -33,7 +33,7 @@ class TutorialIconAction : EventAction
|
||||
}
|
||||
else if(Type == ActionType.Remove)
|
||||
{
|
||||
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.entity == target && i.iconStyle.Equals(IconStyle, System.StringComparison.OrdinalIgnoreCase));
|
||||
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.entity == target && i.iconStyle == IconStyle);
|
||||
}
|
||||
else if (Type == ActionType.RemoveTarget)
|
||||
{
|
||||
@@ -41,7 +41,7 @@ class TutorialIconAction : EventAction
|
||||
}
|
||||
else if (Type == ActionType.RemoveIcon)
|
||||
{
|
||||
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.iconStyle.Equals(IconStyle, System.StringComparison.OrdinalIgnoreCase));
|
||||
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.iconStyle == IconStyle);
|
||||
}
|
||||
else if (Type == ActionType.Clear)
|
||||
{
|
||||
|
||||
+8
-2
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
public SegmentActionType Type { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier Id { get; set; }
|
||||
public Identifier Identifier { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier ObjectiveTag { get; set; }
|
||||
@@ -30,7 +30,13 @@ namespace Barotrauma
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public TutorialSegmentAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
public TutorialSegmentAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
if (Identifier.IsEmpty)
|
||||
{
|
||||
Identifier = element.GetAttributeIdentifier("id", Identifier.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class UIHighlightAction : EventAction
|
||||
{
|
||||
public enum ElementId
|
||||
{
|
||||
None,
|
||||
RepairButton,
|
||||
PumpSpeedSlider,
|
||||
PassiveSonarIndicator,
|
||||
ActiveSonarIndicator,
|
||||
SonarModeSwitch,
|
||||
DirectionalSonarFrame,
|
||||
SteeringModeSwitch,
|
||||
MaintainPosTickBox,
|
||||
AutoTempSwitch,
|
||||
PowerButton,
|
||||
FissionRateSlider,
|
||||
TurbineOutputSlider,
|
||||
DeconstructButton,
|
||||
RechargeSpeedSlider,
|
||||
CPRButton
|
||||
}
|
||||
|
||||
[Serialize(ElementId.None, IsPropertySaveable.Yes)]
|
||||
public ElementId Id { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier EntityIdentifier { get; set; }
|
||||
|
||||
[Serialize(OrderCategory.Emergency, IsPropertySaveable.Yes)]
|
||||
public OrderCategory OrderCategory { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderIdentifier { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderOption { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier OrderTargetTag { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool Bounce { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public UIHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
UpdateProjSpecific();
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific();
|
||||
|
||||
public override bool IsFinished(ref string goToLabel) => isFinished;
|
||||
|
||||
public override void Reset() => isFinished = false;
|
||||
}
|
||||
+7
-14
@@ -329,21 +329,14 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
completed = State > 0 && State != HostagesKilledState;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
else
|
||||
{
|
||||
failed = requireRescue.Any(r => r.Removed || r.IsDead);
|
||||
}
|
||||
return State > 0 && State != HostagesKilledState;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
failed = !completed && requireRescue.Any(r => r.Removed || r.IsDead);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,22 +156,21 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
|
||||
private static bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
|
||||
|
||||
private bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
|
||||
private static bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
bool exitingLevel = GameMain.GameSession?.GameMode is CampaignMode campaign ?
|
||||
campaign.GetAvailableTransition() != CampaignMode.TransitionType.None :
|
||||
Submarine.MainSub is { } sub && (sub.AtEndExit || sub.AtStartExit);
|
||||
|
||||
if (State > 0 && exitingLevel)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
return State > 0 && exitingLevel;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
failed = !completed && State > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,20 +163,16 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
completed = level.CheckBeaconActive();
|
||||
if (completed)
|
||||
return level.CheckBeaconActive();
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
if (completed && level.LevelData != null)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
if (level.LevelData != null)
|
||||
{
|
||||
level.LevelData.IsBeaconActive = true;
|
||||
}
|
||||
level.LevelData.IsBeaconActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Barotrauma
|
||||
else if (sub != this.currentSub || missionsChanged)
|
||||
{
|
||||
this.currentSub = sub;
|
||||
this.nextRoundSubInfo = sub.Info;
|
||||
this.nextRoundSubInfo = sub?.Info;
|
||||
DetermineCargo();
|
||||
}
|
||||
|
||||
@@ -294,22 +294,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.AtEndExit)
|
||||
{
|
||||
int deliveredItemCount = items.Count(it => IsItemDelivered(it));
|
||||
if (deliveredItemCount / (float)items.Count >= requiredDeliveryAmount)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
foreach (Item item in items)
|
||||
{
|
||||
if (!item.Removed) { item.Remove(); }
|
||||
@@ -318,7 +317,7 @@ namespace Barotrauma
|
||||
failed = !completed;
|
||||
}
|
||||
|
||||
private bool IsItemDelivered(Item item)
|
||||
private static bool IsItemDelivered(Item item)
|
||||
{
|
||||
if (item.Removed || item.Condition <= 0.0f || Submarine.MainSub == null) { return false; }
|
||||
var submarine = item.Submarine ?? item.GetRootContainer()?.Submarine;
|
||||
|
||||
@@ -113,15 +113,9 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (GameMain.NetworkMember == null) { return; }
|
||||
|
||||
if (Winner != CharacterTeamType.None)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
return Winner != CharacterTeamType.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace Barotrauma
|
||||
return character.LockHands && character.HasTeamChange(TerroristTeamChangeIdentifier);
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.AtEndExit)
|
||||
{
|
||||
@@ -321,11 +321,14 @@ namespace Barotrauma
|
||||
|
||||
if (friendliesSurvived && !terroristsSurvived && !vipDied)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
if (!IsClient)
|
||||
{
|
||||
foreach (Character character in characters)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class GoToMission : Mission
|
||||
{
|
||||
@@ -11,7 +9,22 @@ namespace Barotrauma
|
||||
|
||||
protected override void UpdateMissionSpecific(float deltaTime)
|
||||
{
|
||||
State = 1;
|
||||
if (Level.Loaded?.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (Level.Loaded?.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Submarine.MainSub is { AtEndExit: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,13 +131,13 @@ namespace Barotrauma
|
||||
|
||||
foreach ((Identifier identifier, ResourceCluster cluster) in resourceClusters)
|
||||
{
|
||||
if (!(MapEntityPrefab.FindByIdentifier(identifier) is ItemPrefab prefab))
|
||||
if (MapEntityPrefab.FindByIdentifier(identifier) is not ItemPrefab prefab)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in MineralMission: couldn't find an item prefab (identifier: \"{identifier}\")");
|
||||
continue;
|
||||
}
|
||||
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, cluster.Amount, positionType, out float rotation);
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, cluster.Amount, positionType, out float rotation, caves);
|
||||
if (spawnedResources.Count < cluster.Amount)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in MineralMission: spawned only {spawnedResources.Count}/{cluster.Amount} of {prefab.Name}");
|
||||
@@ -181,14 +181,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (EnoughHaveBeenCollected())
|
||||
return EnoughHaveBeenCollected();
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
failed = !completed && state > 0;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
if (!IsClient)
|
||||
{
|
||||
// When mission is completed successfully, half of the resources will be removed from the player (i.e. given to the outpost as a part of the mission)
|
||||
@@ -198,7 +200,7 @@ namespace Barotrauma
|
||||
if (relevantLevelResources.TryGetValue(identifier, out var availableResources))
|
||||
{
|
||||
var collectedResources = availableResources.Where(HasBeenCollected);
|
||||
if (collectedResources.Count() < 1) { continue; }
|
||||
if (!collectedResources.Any()) { continue; }
|
||||
int handoverCount = (int)MathF.Round(resourceHandoverAmount * collectedResources.Count());
|
||||
for (int i = 0; i < handoverCount; i++)
|
||||
{
|
||||
@@ -211,8 +213,6 @@ namespace Barotrauma
|
||||
resource.Remove();
|
||||
}
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var kvp in spawnedResources)
|
||||
{
|
||||
@@ -227,7 +227,6 @@ namespace Barotrauma
|
||||
spawnedResources.Clear();
|
||||
relevantLevelResources.Clear();
|
||||
missionClusterPositions.Clear();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
|
||||
private void FindRelevantLevelResources()
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace Barotrauma
|
||||
abstract partial class Mission
|
||||
{
|
||||
public readonly MissionPrefab Prefab;
|
||||
protected bool completed, failed;
|
||||
private bool completed;
|
||||
protected bool failed;
|
||||
|
||||
protected Level level;
|
||||
|
||||
@@ -36,7 +37,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsClient => GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
protected static bool IsClient => GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
|
||||
private readonly CheckDataAction completeCheckDataAction;
|
||||
|
||||
public readonly ImmutableArray<LocalizedString> Headers;
|
||||
public readonly ImmutableArray<LocalizedString> Messages;
|
||||
@@ -156,6 +159,12 @@ namespace Barotrauma
|
||||
|
||||
Locations = locations;
|
||||
|
||||
var endConditionElement = prefab.ConfigElement.GetChildElement(nameof(completeCheckDataAction));
|
||||
if (endConditionElement != null)
|
||||
{
|
||||
completeCheckDataAction = new CheckDataAction(endConditionElement, $"Mission ({prefab.Identifier.ToString()})");
|
||||
}
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
{
|
||||
string locationName = $"‖color:gui.orange‖{locations[n].Name}‖end‖";
|
||||
@@ -334,19 +343,27 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// End the mission and give a reward if it was completed successfully
|
||||
/// </summary>
|
||||
public virtual void End()
|
||||
public void End()
|
||||
{
|
||||
completed = true;
|
||||
completed =
|
||||
DetermineCompleted() &&
|
||||
(completeCheckDataAction == null ||completeCheckDataAction.GetSuccess());
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
EndMissionSpecific(completed);
|
||||
}
|
||||
|
||||
public void GiveReward()
|
||||
protected abstract bool DetermineCompleted();
|
||||
|
||||
protected virtual void EndMissionSpecific(bool completed) { }
|
||||
|
||||
|
||||
private void GiveReward()
|
||||
{
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode campaign)) { return; }
|
||||
if (GameMain.GameSession.GameMode is not CampaignMode campaign) { return; }
|
||||
int reward = GetReward(Submarine.MainSub);
|
||||
|
||||
float baseExperienceGain = reward * 0.09f;
|
||||
|
||||
@@ -146,14 +146,24 @@ namespace Barotrauma
|
||||
|
||||
tags = element.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
|
||||
Name =
|
||||
TextManager.Get($"MissionName.{TextIdentifier}")
|
||||
.Fallback(TextManager.Get(element.GetAttributeString("name", "")))
|
||||
.Fallback(element.GetAttributeString("name", ""));
|
||||
Description =
|
||||
TextManager.Get($"MissionDescription.{TextIdentifier}")
|
||||
.Fallback(TextManager.Get(element.GetAttributeString("description", "")))
|
||||
.Fallback(element.GetAttributeString("description", ""));
|
||||
string nameTag = element.GetAttributeString("name", "");
|
||||
Name = TextManager.Get($"MissionName.{TextIdentifier}");
|
||||
if (!string.IsNullOrEmpty(nameTag))
|
||||
{
|
||||
Name = Name
|
||||
.Fallback(TextManager.Get(nameTag))
|
||||
.Fallback(nameTag);
|
||||
}
|
||||
|
||||
string descriptionTag = element.GetAttributeString("description", "");
|
||||
Description =
|
||||
TextManager.Get($"MissionDescription.{TextIdentifier}");
|
||||
if (!string.IsNullOrEmpty(descriptionTag))
|
||||
{
|
||||
Description = Description
|
||||
.Fallback(TextManager.Get(descriptionTag))
|
||||
.Fallback(descriptionTag);
|
||||
}
|
||||
|
||||
Reward = element.GetAttributeInt("reward", 1);
|
||||
AllowRetry = element.GetAttributeBool("allowretry", false);
|
||||
@@ -167,23 +177,35 @@ namespace Barotrauma
|
||||
Difficulty = Math.Clamp(difficulty, MinDifficulty, MaxDifficulty);
|
||||
}
|
||||
|
||||
SuccessMessage =
|
||||
TextManager.Get($"MissionSuccess.{TextIdentifier}")
|
||||
.Fallback(TextManager.Get(element.GetAttributeString("successmessage", "")))
|
||||
.Fallback(element.GetAttributeString("successmessage", "Mission completed successfully"));
|
||||
FailureMessage =
|
||||
TextManager.Get($"MissionFailure.{TextIdentifier}")
|
||||
.Fallback(TextManager.Get(element.GetAttributeString("missionfailed", "")))
|
||||
.Fallback(TextManager.Get("missionfailed"))
|
||||
.Fallback(GameSettings.CurrentConfig.Language == TextManager.DefaultLanguage ? element.GetAttributeString("failuremessage", "") : "");
|
||||
string successMessageTag = element.GetAttributeString("successmessage", "");
|
||||
SuccessMessage = TextManager.Get($"MissionSuccess.{TextIdentifier}");
|
||||
if (!string.IsNullOrEmpty(successMessageTag))
|
||||
{
|
||||
SuccessMessage = SuccessMessage
|
||||
.Fallback(TextManager.Get(successMessageTag))
|
||||
.Fallback(successMessageTag);
|
||||
}
|
||||
SuccessMessage = SuccessMessage.Fallback(TextManager.Get("missioncompleted"));
|
||||
|
||||
string failureMessageTag = element.GetAttributeString("failuremessage", "");
|
||||
FailureMessage = TextManager.Get($"MissionFailure.{TextIdentifier}");
|
||||
if (!string.IsNullOrEmpty(failureMessageTag))
|
||||
{
|
||||
FailureMessage = FailureMessage
|
||||
.Fallback(TextManager.Get(failureMessageTag))
|
||||
.Fallback(failureMessageTag);
|
||||
}
|
||||
FailureMessage = FailureMessage.Fallback(TextManager.Get("missionfailed"));
|
||||
|
||||
string sonarLabelTag = element.GetAttributeString("sonarlabel", "");
|
||||
|
||||
SonarLabel =
|
||||
SonarLabel =
|
||||
TextManager.Get($"MissionSonarLabel.{sonarLabelTag}")
|
||||
.Fallback(TextManager.Get(sonarLabelTag))
|
||||
.Fallback(TextManager.Get($"MissionSonarLabel.{TextIdentifier}"))
|
||||
.Fallback(element.GetAttributeString("sonarlabel", ""));
|
||||
.Fallback(TextManager.Get($"MissionSonarLabel.{TextIdentifier}"));
|
||||
if (!string.IsNullOrEmpty(sonarLabelTag))
|
||||
{
|
||||
SonarLabel = SonarLabel.Fallback(sonarLabelTag);
|
||||
}
|
||||
|
||||
SonarIconIdentifier = element.GetAttributeIdentifier("sonaricon", "");
|
||||
|
||||
|
||||
@@ -223,26 +223,26 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
return state > 0;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
tempSonarPositions.Clear();
|
||||
monsters.Clear();
|
||||
if (State < 1) { return; }
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
if (completed)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (level?.LevelData != null && Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
level.LevelData.HasHuntingGrounds = false;
|
||||
if (level?.LevelData != null && Prefab.Tags.Contains("huntinggrounds"))
|
||||
{
|
||||
level.LevelData.HasHuntingGrounds = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEliminated(Character enemy) =>
|
||||
public static bool IsEliminated(Character enemy) =>
|
||||
enemy == null ||
|
||||
enemy.Removed ||
|
||||
enemy.IsDead ||
|
||||
|
||||
@@ -305,20 +305,13 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
return AllItemsDestroyedOrRetrieved();
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
if (AllItemsDestroyedOrRetrieved())
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Item item in items)
|
||||
{
|
||||
if (item != null && !item.Removed)
|
||||
|
||||
@@ -401,13 +401,13 @@ namespace Barotrauma
|
||||
return character == null || character.Removed || character.Submarine == null || (character.LockHands && character.Submarine == Submarine.MainSub) || character.IsIncapacitated;
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
return state == 2;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
if (state == 2)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
characters.Clear();
|
||||
characterItems.Clear();
|
||||
failed = !completed;
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace Barotrauma
|
||||
Submarine parentSub = item.CurrentHull?.Submarine ?? item.GetRootInventoryOwner()?.Submarine;
|
||||
if (parentSub == null || parentSub.Info.Type != SubmarineType.Player)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
State = 1;
|
||||
@@ -254,23 +254,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
var root = item?.GetRootContainer() ?? item;
|
||||
if (root?.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
return root?.CurrentHull?.Submarine != null && (root.CurrentHull.Submarine.AtEndExit || root.CurrentHull.Submarine.AtStartExit) && !item.Removed;
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
item?.Remove();
|
||||
item = null;
|
||||
GiveReward();
|
||||
completed = true;
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,24 +247,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
protected override bool DetermineCompleted()
|
||||
{
|
||||
if (State == 2 && AllScannersReturned())
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var scanner in scanners)
|
||||
{
|
||||
if (scanner.Item != null && !scanner.Item.Removed)
|
||||
{
|
||||
scanner.OnScanStarted -= OnScanStarted;
|
||||
scanner.OnScanCompleted -= OnScanCompleted;
|
||||
scanner.Item.Remove();
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
failed = !completed && state > 0;
|
||||
return State == 2 && AllScannersReturned();
|
||||
|
||||
bool AllScannersReturned()
|
||||
{
|
||||
@@ -285,5 +270,20 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EndMissionSpecific(bool completed)
|
||||
{
|
||||
foreach (var scanner in scanners)
|
||||
{
|
||||
if (scanner.Item != null && !scanner.Item.Removed)
|
||||
{
|
||||
scanner.OnScanStarted -= OnScanStarted;
|
||||
scanner.OnScanCompleted -= OnScanCompleted;
|
||||
scanner.Item.Remove();
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly Identifier SpeciesName;
|
||||
public readonly int MinAmount, MaxAmount;
|
||||
private List<Character> monsters;
|
||||
private readonly List<Character> monsters = new List<Character>();
|
||||
|
||||
private readonly float scatter;
|
||||
private readonly float offset;
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly int MaxAmountPerLevel = int.MaxValue;
|
||||
|
||||
public List<Character> Monsters => monsters;
|
||||
public IReadOnlyList<Character> Monsters => monsters;
|
||||
public Vector2? SpawnPos => spawnPos;
|
||||
public bool SpawnPending => spawnPending;
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Submarine GetReferenceSub()
|
||||
private static Submarine GetReferenceSub()
|
||||
{
|
||||
return EventManager.GetRefEntity() as Submarine ?? Submarine.MainSub;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ namespace Barotrauma
|
||||
DebugConsole.NewMessage("Initialized MonsterEvent (" + SpeciesName + ")", Color.White);
|
||||
}
|
||||
|
||||
monsters = new List<Character>();
|
||||
monsters.Clear();
|
||||
|
||||
//+1 because Range returns an integer less than the max value
|
||||
int amount = Rand.Range(MinAmount, MaxAmount + 1);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ID != Entity.NullEntityID)
|
||||
{
|
||||
DebugConsole.ShowError("Error setting SoldItem.ID: ID has already been set and should not be changed.");
|
||||
DebugConsole.LogError("Error setting SoldItem.ID: ID has already been set and should not be changed.");
|
||||
return;
|
||||
}
|
||||
ID = id;
|
||||
@@ -128,7 +128,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Item != null)
|
||||
{
|
||||
DebugConsole.ShowError($"Trying to set SoldEntity.Item, but it's already set!\n{Environment.StackTrace.CleanupStackTrace()}");
|
||||
DebugConsole.LogError($"Trying to set SoldEntity.Item, but it's already set!\n{Environment.StackTrace.CleanupStackTrace()}");
|
||||
return;
|
||||
}
|
||||
Item = item;
|
||||
|
||||
@@ -198,6 +198,34 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the character from the crew (and crew menus).
|
||||
/// </summary>
|
||||
/// <param name="character">The character to remove</param>
|
||||
/// <param name="removeInfo">If the character info is also removed, the character will not be visible in the round summary.</param>
|
||||
public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
|
||||
{
|
||||
if (character == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return;
|
||||
}
|
||||
characters.Remove(character);
|
||||
if (removeInfo)
|
||||
{
|
||||
characterInfos.Remove(character.Info);
|
||||
#if CLIENT
|
||||
RemoveCharacterFromCrewList(character);
|
||||
#endif
|
||||
}
|
||||
#if CLIENT
|
||||
if (resetCrewListIndex)
|
||||
{
|
||||
ResetCrewListIndex(character);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddCharacterInfo(CharacterInfo characterInfo)
|
||||
{
|
||||
if (characterInfos.Contains(characterInfo))
|
||||
|
||||
+8
@@ -27,6 +27,8 @@ namespace Barotrauma
|
||||
|
||||
public readonly Identifier EventIdentifier;
|
||||
|
||||
public readonly Sprite Banner;
|
||||
|
||||
public TutorialPrefab(ContentFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
Order = element.GetAttributeInt("order", int.MaxValue);
|
||||
@@ -50,6 +52,12 @@ namespace Barotrauma
|
||||
StartingItemTags = ImmutableArray<Identifier>.Empty;
|
||||
}
|
||||
|
||||
var bannerElement = element.GetChildElement("banner");
|
||||
if (bannerElement != null)
|
||||
{
|
||||
Banner = new Sprite(bannerElement, lazyLoad: true);
|
||||
}
|
||||
|
||||
EventIdentifier = element.GetChildElement("scriptedevent")?.GetAttributeIdentifier("identifier", "") ?? Identifier.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -412,14 +412,14 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public List<Item> GetLinkedItemsToSwap(Item item)
|
||||
public static ICollection<Item> GetLinkedItemsToSwap(Item item)
|
||||
{
|
||||
List<Item> linkedItems = new List<Item>() { item };
|
||||
HashSet<Item> linkedItems = new HashSet<Item>() { item };
|
||||
foreach (MapEntity linkedEntity in item.linkedTo)
|
||||
{
|
||||
foreach (MapEntity secondLinkedEntity in linkedEntity.linkedTo)
|
||||
{
|
||||
if (!(secondLinkedEntity is Item linkedItem) || linkedItem == item) { continue; }
|
||||
if (secondLinkedEntity is not Item linkedItem || linkedItem == item) { continue; }
|
||||
if (linkedItem.AllowSwapping &&
|
||||
linkedItem.Prefab.SwappableItem != null && (linkedItem.Prefab.SwappableItem.CanBeBought || item.Prefab.SwappableItem.ReplacementOnUninstall == ((MapEntity)linkedItem).Prefab.Identifier) &&
|
||||
linkedItem.Prefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase))
|
||||
@@ -709,7 +709,7 @@ namespace Barotrauma
|
||||
SavePendingUpgrades(upgradeManagerElement, PendingUpgrades);
|
||||
}
|
||||
|
||||
private void SavePendingUpgrades(XElement? parent, List<PurchasedUpgrade> upgrades)
|
||||
private static void SavePendingUpgrades(XElement? parent, List<PurchasedUpgrade> upgrades)
|
||||
{
|
||||
if (parent == null) { return; }
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
private float forceLockTimer;
|
||||
//if the submarine isn't in the correct position to lock within this time after docking has been activated,
|
||||
//force the sub to the correct position
|
||||
const float ForceLockDelay = 1.0f;
|
||||
const float ForceLockDelay = 1.0f;
|
||||
|
||||
public int DockingDir { get; set; }
|
||||
|
||||
@@ -81,12 +81,18 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(DirectionType.None, IsPropertySaveable.No, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
||||
[Editable, Serialize(DirectionType.None, IsPropertySaveable.No, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n" +
|
||||
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
||||
public DirectionType ForceDockingDirection { get; set; }
|
||||
|
||||
|
||||
public DockingPort DockingTarget { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects
|
||||
/// </summary>
|
||||
public bool AtStartExit => Item.Submarine is { AtStartExit: true};
|
||||
public bool AtEndExit => Item.Submarine is { AtEndExit: true };
|
||||
|
||||
public Door Door { get; private set; }
|
||||
|
||||
public bool Docked
|
||||
@@ -116,6 +122,8 @@ namespace Barotrauma.Items.Components
|
||||
get { return joint is WeldJoint || DockingTarget?.joint is WeldJoint; }
|
||||
}
|
||||
|
||||
public bool AnotherPortInProximity => FindAdjacentPort() != null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically cleared after docking -> no need to unregister
|
||||
/// </summary>
|
||||
@@ -989,7 +997,7 @@ namespace Barotrauma.Items.Components
|
||||
dockingState = MathHelper.Lerp(dockingState, 0.0f, deltaTime * 10.0f);
|
||||
if (dockingState < 0.01f) { docked = false; }
|
||||
item.SendSignal("0", "state_out");
|
||||
item.SendSignal((FindAdjacentPort() != null) ? "1" : "0", "proximity_sensor");
|
||||
item.SendSignal(AnotherPortInProximity ? "1" : "0", "proximity_sensor");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GetAvailableInstantaneousBatteryPower() >= PowerConsumption)
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float neededPower = PowerConsumption;
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
|
||||
@@ -111,6 +111,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
//return if the status effect got rid of the picker somehow
|
||||
if (picker == null || picker.Removed || !picker.HeldItems.Contains(item))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) { item.FlipX(relativeToSub: false); }
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool alwaysContainedItemsSpawned;
|
||||
|
||||
public ItemInventory Inventory;
|
||||
public readonly ItemInventory Inventory;
|
||||
|
||||
private readonly List<ActiveContainedItem> activeContainedItems = new List<ActiveContainedItem>();
|
||||
|
||||
@@ -189,6 +189,16 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects to lock the inventory
|
||||
/// </summary>
|
||||
public bool Locked
|
||||
{
|
||||
get { return Inventory.Locked; }
|
||||
set { Inventory.Locked = value; }
|
||||
}
|
||||
|
||||
private readonly ImmutableArray<SlotRestrictions> slotRestrictions;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
@@ -136,6 +136,13 @@ namespace Barotrauma.Items.Components
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
set
|
||||
{
|
||||
zoom = MathHelper.Clamp(value, MinZoom, MaxZoom);
|
||||
#if CLIENT
|
||||
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public Mode CurrentMode
|
||||
|
||||
@@ -278,7 +278,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? PowerConsumption + ExtraLoad : 0;
|
||||
//not used in the vanilla game (junction boxes or relays don't output power)
|
||||
return conn == powerOut ? MathHelper.Max(-(PowerConsumption + ExtraLoad), 0) : 0;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -690,43 +690,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Efficient method to retrieve the batteries connected to the device
|
||||
/// Returns a list of batteries directly connected to the item
|
||||
/// </summary>
|
||||
/// <returns>All connected PowerContainers</returns>
|
||||
protected List<PowerContainer> GetConnectedBatteries(bool outputOnly = true)
|
||||
protected List<PowerContainer> GetDirectlyConnectedBatteries()
|
||||
{
|
||||
List<PowerContainer> batteries = new List<PowerContainer>();
|
||||
GridInfo supplyingGrid = null;
|
||||
|
||||
//Determine supplying grid, prefer PowerIn connection
|
||||
if (powerIn != null)
|
||||
if (item.Connections == null || powerIn == null) { return batteries; }
|
||||
foreach (Connection recipient in powerIn.Recipients)
|
||||
{
|
||||
if (powerIn.Grid != null)
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery != null)
|
||||
{
|
||||
supplyingGrid = powerIn.Grid;
|
||||
batteries.Add(battery);
|
||||
}
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerOut.Grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (supplyingGrid != null)
|
||||
{
|
||||
//Iterate through all connections to fine powerContainers
|
||||
foreach (Connection c in supplyingGrid.Connections)
|
||||
{
|
||||
PowerContainer pc = c.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && (!outputOnly || pc.powerOut == c))
|
||||
{
|
||||
batteries.Add(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batteries;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,7 @@ namespace Barotrauma.Items.Components
|
||||
FirepowerMultiplier,
|
||||
StrikingPowerMultiplier,
|
||||
StrikingSpeedMultiplier,
|
||||
FiringRateMultiplier,
|
||||
// unused as of now
|
||||
AttackMultiplier,
|
||||
// unused as of now
|
||||
AttackSpeedMultiplier,
|
||||
ForceDoorsOpenSpeedMultiplier,
|
||||
RangedSpreadReduction,
|
||||
ChargeSpeedMultiplier,
|
||||
MovementSpeedMultiplier,
|
||||
EffectivenessMultiplier,
|
||||
PowerOutputMultiplier,
|
||||
ConsumptionReductionMultiplier,
|
||||
FiringRateMultiplier
|
||||
}
|
||||
|
||||
private readonly Dictionary<StatType, float> statValues = new Dictionary<StatType, float>();
|
||||
|
||||
@@ -603,6 +603,9 @@ namespace Barotrauma.Items.Components
|
||||
private bool ShouldDeteriorate()
|
||||
{
|
||||
if (Level.IsLoadedFriendlyOutpost) { return false; }
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode) { return false; }
|
||||
#endif
|
||||
|
||||
if (LastActiveTime > Timing.TotalTime) { return true; }
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
|
||||
+4
@@ -48,6 +48,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (value == null) { return; }
|
||||
output = value;
|
||||
//reactivate (we may not have been previously sending a signal, but might now)
|
||||
IsActive = true;
|
||||
if (output.Length > MaxOutputLength && (item.Submarine == null || !item.Submarine.Loading))
|
||||
{
|
||||
output = output.Substring(0, MaxOutputLength);
|
||||
@@ -63,6 +65,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (value == null) { return; }
|
||||
falseOutput = value;
|
||||
//reactivate (we may not have been previously sending a signal, but might now)
|
||||
IsActive = true;
|
||||
if (falseOutput.Length > MaxOutputLength && (item.Submarine == null || !item.Submarine.Loading))
|
||||
{
|
||||
falseOutput = falseOutput.Substring(0, MaxOutputLength);
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace Barotrauma.Items.Components
|
||||
Human = 1,
|
||||
Monster = 2,
|
||||
Wall = 4,
|
||||
Any = Human | Monster | Wall,
|
||||
Pet = 8,
|
||||
Any = Human | Monster | Wall | Pet,
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
@@ -253,7 +254,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Monster))
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Pet) || Target.HasFlag(TargetType.Monster))
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
@@ -267,7 +268,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Human)) { continue; }
|
||||
}
|
||||
else if (!c.IsPet)
|
||||
else if (c.IsPet)
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Pet)) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Monster)) { continue; }
|
||||
}
|
||||
|
||||
@@ -702,7 +702,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!ignorePower)
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float neededPower = GetPowerRequiredToShoot();
|
||||
|
||||
// tinkering is currently not factored into the common method as it is checked only when shooting
|
||||
@@ -1066,7 +1066,7 @@ namespace Barotrauma.Items.Components
|
||||
bool canShoot = true;
|
||||
if (!HasPowerToShoot())
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float lowestCharge = 0.0f;
|
||||
PowerContainer batteryToLoad = null;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
@@ -1308,9 +1308,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
float dist = Vector2.Distance(closestPoint, item.WorldPosition);
|
||||
|
||||
//add one px to make sure the visibility raycast doesn't miss the cell due to the end position being right at the edge of the cell
|
||||
closestPoint += (closestPoint - item.WorldPosition) / Math.Max(dist, 1);
|
||||
|
||||
if (dist > AIRange + 1000) { continue; }
|
||||
float dot = 0;
|
||||
if (item.Submarine.Velocity != Vector2.Zero)
|
||||
if (!MathUtils.NearlyEqual(item.Submarine.Velocity, Vector2.Zero))
|
||||
{
|
||||
dot = Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), Vector2.Normalize(closestPoint - item.Submarine.WorldPosition));
|
||||
}
|
||||
|
||||
@@ -422,6 +422,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Color? HighlightColor;
|
||||
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
|
||||
@@ -523,6 +525,7 @@ namespace Barotrauma
|
||||
{
|
||||
float prevConditionPercentage = ConditionPercentage;
|
||||
healthMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity);
|
||||
RecalculateConditionValues();
|
||||
condition = MaxCondition * prevConditionPercentage / 100.0f;
|
||||
RecalculateConditionValues();
|
||||
}
|
||||
@@ -751,6 +754,9 @@ namespace Barotrauma
|
||||
get { return Prefab.Linkable; }
|
||||
}
|
||||
|
||||
public float WorldPositionX => WorldPosition.X;
|
||||
public float WorldPositionY => WorldPosition.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to move the item from XML (e.g. to correct the positions of items whose sprite origin has been changed)
|
||||
/// </summary>
|
||||
@@ -1913,8 +1919,11 @@ namespace Barotrauma
|
||||
if (!wasInWater && CurrentHull != null && body != null && body.LinearVelocity.Y < -1.0f)
|
||||
{
|
||||
Splash();
|
||||
//slow the item down (not physically accurate, but looks good enough)
|
||||
body.LinearVelocity *= 0.2f;
|
||||
if (GetComponent<Projectile>() is not { IsActive: true })
|
||||
{
|
||||
//slow the item down (not physically accurate, but looks good enough)
|
||||
body.LinearVelocity *= 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
Item container = this.Container;
|
||||
@@ -3340,10 +3349,8 @@ namespace Barotrauma
|
||||
item.PurchasedNewSwap = false;
|
||||
}
|
||||
|
||||
float condition = element.GetAttributeFloat("condition", item.MaxCondition);
|
||||
item.condition = MathHelper.Clamp(condition, 0, item.MaxCondition);
|
||||
item.condition = MathHelper.Clamp(item.condition, 0, item.MaxCondition);
|
||||
item.lastSentCondition = item.condition;
|
||||
|
||||
item.RecalculateConditionValues();
|
||||
item.SetActiveSprite();
|
||||
|
||||
|
||||
@@ -1123,7 +1123,7 @@ namespace Barotrauma
|
||||
{
|
||||
string message = $"Tried to get price info for \"{Identifier}\" with a null store parameter!\n{Environment.StackTrace.CleanupStackTrace()}";
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError(message);
|
||||
DebugConsole.LogError(message);
|
||||
#else
|
||||
DebugConsole.AddWarning(message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ItemPrefab.GetPriceInfo:StoreParameterNull", GameAnalyticsManager.ErrorSeverity.Error, message);
|
||||
|
||||
@@ -2932,7 +2932,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="rotation">Used by clients to set the rotation for the resources</param>
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, out float rotation)
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, out float rotation, IEnumerable<Cave> targetCaves = null)
|
||||
{
|
||||
var allValidLocations = GetAllValidClusterLocations();
|
||||
var placedResources = new List<Item>();
|
||||
@@ -2995,6 +2995,12 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Unexpected PositionType (\"{positionType}\") for mineral mission resources: mineral spawning might not work as expected.");
|
||||
}
|
||||
|
||||
if (targetCaves != null && targetCaves.Any())
|
||||
{
|
||||
// If resources are placed inside a cave, make sure all of them are placed inside the same one
|
||||
allValidLocations.RemoveAll(l => targetCaves.None(c => c.Area.Contains(l.EdgeCenter)));
|
||||
}
|
||||
|
||||
var poi = PositionsOfInterest.GetRandom(p => p.PositionType == positionType, randSync: Rand.RandSync.ServerAndClient);
|
||||
Vector2 poiPos = poi.Position.ToVector2();
|
||||
allValidLocations.Sort((x, y) => Vector2.DistanceSquared(poiPos, x.EdgeCenter)
|
||||
@@ -3002,7 +3008,10 @@ namespace Barotrauma
|
||||
float maxResourceOverlap = 0.4f;
|
||||
var selectedLocation = allValidLocations.FirstOrDefault(l =>
|
||||
Vector2.Distance(l.Edge.Point1, l.Edge.Point2) is float edgeLength &&
|
||||
!l.Edge.OutsideLevel &&
|
||||
requiredAmount <= (int)Math.Floor(edgeLength / ((1.0f - maxResourceOverlap) * prefab.Size.X)));
|
||||
|
||||
|
||||
if (selectedLocation.Edge == null)
|
||||
{
|
||||
//couldn't find a long enough edge, find the largest one
|
||||
@@ -3028,10 +3037,10 @@ namespace Barotrauma
|
||||
static bool IsOnMainPath(ClusterLocation location) => location.Edge.NextToMainPath;
|
||||
static bool IsOnSidePath(ClusterLocation location) => location.Edge.NextToSidePath;
|
||||
static bool IsInCave(ClusterLocation location) => location.Edge.NextToCave;
|
||||
bool IsInAbyssCave(ClusterLocation location) => location.EdgeCenter.Y > AbyssArea.Bottom;
|
||||
bool IsInAbyssCave(ClusterLocation location) => location.EdgeCenter.Y < AbyssStart;
|
||||
void RemoveInvalidLocations(Predicate<ClusterLocation> match)
|
||||
{
|
||||
allValidLocations.RemoveAll(match);
|
||||
allValidLocations.RemoveAll(l => !match(l));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -281,6 +281,10 @@ namespace Barotrauma
|
||||
IdRemap parentRemap = new IdRemap(Submarine.Info.SubmarineElement, Submarine.IdOffset);
|
||||
sub = Submarine.Load(info, false, parentRemap);
|
||||
sub.Info.SubmarineClass = Submarine.Info.SubmarineClass;
|
||||
if (Submarine.Info.IsOutpost && Submarine.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
sub.TeamID = CharacterTeamType.FriendlyNPC;
|
||||
}
|
||||
|
||||
IdRemap childRemap = new IdRemap(saveElement, sub.IdOffset);
|
||||
|
||||
|
||||
@@ -700,7 +700,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public MissionPrefab UnlockMissionByIdentifier(Identifier identifier)
|
||||
public Mission UnlockMissionByIdentifier(Identifier identifier)
|
||||
{
|
||||
if (AvailableMissions.Any(m => m.Prefab.Identifier == identifier)) { return null; }
|
||||
|
||||
@@ -721,17 +721,17 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.Campaign?.CampaignUI?.RefreshLocationInfo();
|
||||
#endif
|
||||
return missionPrefab;
|
||||
return mission;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MissionPrefab UnlockMissionByTag(Identifier tag)
|
||||
public Mission UnlockMissionByTag(Identifier tag)
|
||||
{
|
||||
var matchingMissions = MissionPrefab.Prefabs.Where(mp => mp.Tags.Any(t => t == tag));
|
||||
if (!matchingMissions.Any())
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to unlock a mission with the tag \"{tag}\": no matching missions not found.");
|
||||
DebugConsole.ThrowError($"Failed to unlock a mission with the tag \"{tag}\": no matching missions found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -754,7 +754,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.Campaign?.CampaignUI?.RefreshLocationInfo();
|
||||
#endif
|
||||
return missionPrefab;
|
||||
return mission;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -462,6 +462,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the connections are in the same order on the locations and the Connections list
|
||||
//otherwise their order will change when loading the game (as they're added to the locations in the same order they're loaded)
|
||||
foreach (var location in Locations)
|
||||
{
|
||||
location.Connections.Sort((c1, c2) => Connections.IndexOf(c1).CompareTo(Connections.IndexOf(c2)));
|
||||
}
|
||||
|
||||
for (int i = Connections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
i = Math.Min(i, Connections.Count - 1);
|
||||
|
||||
@@ -1234,7 +1234,7 @@ namespace Barotrauma
|
||||
public List<(ItemContainer container, int freeSlots)> GetCargoContainers()
|
||||
{
|
||||
List<(ItemContainer container, int freeSlots)> containers = new List<(ItemContainer container, int freeSlots)>();
|
||||
var connectedSubs = GetConnectedSubs();
|
||||
var connectedSubs = GetConnectedSubs().Where(sub => sub.Info?.Type == Info.Type);
|
||||
foreach (Item item in Item.ItemList.ToList())
|
||||
{
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
|
||||
@@ -447,11 +447,13 @@ namespace Barotrauma
|
||||
|
||||
private Vector2 CalculateBuoyancy()
|
||||
{
|
||||
if (Submarine.LockY) { return Vector2.Zero; }
|
||||
|
||||
float waterVolume = 0.0f;
|
||||
float volume = 0.0f;
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != submarine) continue;
|
||||
if (hull.Submarine != submarine) { continue; }
|
||||
|
||||
waterVolume += hull.WaterVolume;
|
||||
volume += hull.Volume;
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Barotrauma.Networking
|
||||
public bool InGame;
|
||||
public bool HasPermissions;
|
||||
public bool IsOwner;
|
||||
public bool AllowKicking;
|
||||
public bool IsDownloading;
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -101,34 +101,45 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
public LocalizedString ChatMessage(Client c)
|
||||
=> DisconnectReason switch
|
||||
{
|
||||
LocalizedString message = DisconnectReason switch
|
||||
{
|
||||
DisconnectReason.Disconnected => TextManager.GetWithVariable("ServerMessage.ClientLeftServer",
|
||||
"[client]", c.Name),
|
||||
DisconnectReason.Banned => TextManager.GetWithVariable("servermessage.bannedfromserver", "[client]", c.Name),
|
||||
DisconnectReason.Kicked => TextManager.GetWithVariable("servermessage.kickedfromserver", "[client]", c.Name),
|
||||
_ => TextManager.GetWithVariables("ChatMsg.DisconnectedWithReason",
|
||||
("[client]", c.Name),
|
||||
("[reason]", TextManager.Get($"ChatMsg.DisconnectReason.{DisconnectReason}")))
|
||||
};
|
||||
if (!string.IsNullOrEmpty(AdditionalInformation) &&
|
||||
DisconnectReason is DisconnectReason.Banned or DisconnectReason.Kicked)
|
||||
{
|
||||
message += " "+ TextManager.Get("banreason") + " " + TextManager.GetServerMessage(AdditionalInformation);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private LocalizedString msgWithReason
|
||||
|
||||
private LocalizedString MsgWithReason
|
||||
=> TextManager.Get($"DisconnectReason.{DisconnectReason}")
|
||||
+ "\n\n"
|
||||
+ TextManager.Get("banreason") + " " + AdditionalInformation;
|
||||
+ TextManager.Get("banreason") + " " + TextManager.GetServerMessage(AdditionalInformation);
|
||||
|
||||
private LocalizedString serverMessage
|
||||
private LocalizedString ServerMessage
|
||||
=> TextManager.Get($"ServerMessage.{DisconnectReason}");
|
||||
|
||||
public LocalizedString PopupMessage
|
||||
=> DisconnectReason switch
|
||||
{
|
||||
DisconnectReason.Banned => msgWithReason,
|
||||
DisconnectReason.Kicked => msgWithReason,
|
||||
DisconnectReason.Banned => MsgWithReason,
|
||||
DisconnectReason.Kicked => MsgWithReason,
|
||||
DisconnectReason.InvalidVersion => TextManager.GetWithVariables("DisconnectMessage.InvalidVersion",
|
||||
("[version]", AdditionalInformation),
|
||||
("[clientversion]", GameMain.Version.ToString())),
|
||||
DisconnectReason.ExcessiveDesyncOldEvent => serverMessage,
|
||||
DisconnectReason.ExcessiveDesyncRemovedEvent => serverMessage,
|
||||
DisconnectReason.SyncTimeout => serverMessage,
|
||||
DisconnectReason.ExcessiveDesyncOldEvent => ServerMessage,
|
||||
DisconnectReason.ExcessiveDesyncRemovedEvent => ServerMessage,
|
||||
DisconnectReason.SyncTimeout => ServerMessage,
|
||||
_ => TextManager.Get($"DisconnectReason.{DisconnectReason}").Fallback(TextManager.Get("ConnectionLost"))
|
||||
};
|
||||
|
||||
@@ -165,9 +176,6 @@ namespace Barotrauma.Networking
|
||||
or DisconnectReason.TooManyFailedLogins
|
||||
or DisconnectReason.InvalidVersion);
|
||||
|
||||
public bool ShouldShowMessage
|
||||
=> DisconnectReason is not DisconnectReason.Disconnected;
|
||||
|
||||
private const string lidgrenSeparator = ":hankey:";
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -77,13 +77,13 @@ namespace Barotrauma
|
||||
Projectile projectile = (entity as Item)?.GetComponent<Projectile>();
|
||||
if (projectile == null)
|
||||
{
|
||||
DebugConsole.ShowError("Non-projectile using a delaytype of reachcursor");
|
||||
DebugConsole.LogError("Non-projectile using a delaytype of reachcursor");
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectile.User == null)
|
||||
{
|
||||
DebugConsole.ShowError("Projectile: '" + projectile.Name + "' missing user to determine distance");
|
||||
DebugConsole.LogError("Projectile: '" + projectile.Name + "' missing user to determine distance");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace Barotrauma
|
||||
if (projectile == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError("Non-projectile using a delaytype of reachcursor");
|
||||
DebugConsole.LogError("Non-projectile using a delaytype of reachcursor");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ namespace Barotrauma
|
||||
if (projectile.User == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError("Projectile " + projectile.Name + "missing user");
|
||||
DebugConsole.LogError("Projectile " + projectile.Name + "missing user");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1770,7 +1770,7 @@ namespace Barotrauma
|
||||
case ItemSpawnInfo.SpawnRotationType.Random:
|
||||
if (projectile != null)
|
||||
{
|
||||
DebugConsole.ShowError("Random rotation is not supported for Projectiles.");
|
||||
DebugConsole.LogError("Random rotation is not supported for Projectiles.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,401 +1,305 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.5.0
|
||||
v0.19.8.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Progress on the new tutorials, the Roles tutorials in particular. Still a work in progress, but feedback is again more than welcome!
|
||||
- Cleaned up networking and server list code to make them less error-prone and easier to work with in the future. Should not cause any functional differences, but please let us know if you notice any issues or oddities!
|
||||
- Fixes and improvements to Camel.
|
||||
- Fixed skill texts not being colored according to the user's skills in the repair UI.
|
||||
- Fixed outpost service NPCs' titles (which are also used as the tooltips on the icons) not showing up in the multiplayer campaign.
|
||||
- Further fixes to dragging animations.
|
||||
- Fixed warning about duplicate keybinds being displayed for things set to None.
|
||||
- Fixed clients who've gotten vote kicked getting vote kicked again when they join.
|
||||
- Fixed favorited localhost servers causing a crash.
|
||||
- Fixed respawn shuttle setting toggling on and off when adjusting other settings.
|
||||
- Fixed skills increasing on death if the skill level is under the maximum initial skill.
|
||||
- Fixes duplicate Captain Hognoses occasionally appearing in the crew list when selecting the Hognose mission in the multiplayer campaign.
|
||||
- Fixed wrecked coilgun and railgun launch impulses.
|
||||
- Fixed delayed status effects not working after the parent entity has been removed (breaking many medical items, e.g. ethanol).
|
||||
- Fixed inability to buy anything from other stores when you've bought something from one of the stores in an outpost in the multiplayer campaign.
|
||||
- Minor improvements and fixes to the tutorials (e.g. safeguards to prevent dying or getting stuck).
|
||||
- Fixed Typhon 2's chaingun being placed inside solid walls.
|
||||
- Fixed Tandem Fire talent causing a crash if there's no allies alive.
|
||||
- Fixed bots being unable to shoot at ice spires with pulse laser or chaingun.
|
||||
- Removed outdated Deep Diver loading screen tip.
|
||||
- Fixed misaligned connection panel interface when repairing a status monitor.
|
||||
- Fixed Winterhalter battery recharge speed being limited by the relay that supplies power to them, making recharge speed upgrades useless.
|
||||
- Fixed a networking issue that caused afflictions' periodic effects (e.g. nausea-induced vomiting) to happen too frequently client-side.
|
||||
- Fixed none of the contextual orders except "wait here" showing the name of the order in the tooltip.
|
||||
- Fixed inconsistencies in some crawler swarm mission names (large swarms not being described as large).
|
||||
- Fixed mineral mission resources sometimes spawning in separate caves.
|
||||
- Fixed minerals mission resources sometimes spawning outside the level.
|
||||
- Moved handheld sonar's drag icon to a more appropriate position.
|
||||
- Fixed buoyancy making locked subs slowly move vertically.
|
||||
- Reactors don't explode if they reach 0 condition without fuel.
|
||||
- Fixed motion sensor not detecting pets.
|
||||
- Fixed cargo capacity displayed in the mission selection screen including the cargo containers in the outpost the sub is docked.
|
||||
- Fixed clients being unable to select the respawn shuttle if they have to download it from the server.
|
||||
- Fixed duplicate banlist entires when a client gets banned due to an incorrect password.
|
||||
- Some wire and waypoint cleanup to R-29 and Remora.
|
||||
- Removed a wall in Remora to allow for better movement inside.
|
||||
- Changed one railgun shelf to boxes shelf on Remora.
|
||||
- Added a coilgun and a couple more diving suit cabinets to R-29.
|
||||
- Fixed "completed initialization before receiving content package order" error when trying to reconnect to a SteamP2P server.
|
||||
|
||||
Changes:
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.7.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Changes and additions:
|
||||
- Completely remade tutorials. There is now a Basics Tutorial to cover basics like moving, inventory and repairs. More specific tasks are explained in the Roles Tutorial, where every job has their own tutorial to go through, explaining what it means to be e.g. an Engineer or a Captain.
|
||||
- Added new mining missions, including some in the abyss.
|
||||
- Reintroduced separate local/radio voice chat keys as a legacy option. Now it's again possible to speak with voice activation by default and use a push-to-talk button for radio, the same way as before, by setting the chat mode to Local and using the new radio voice chat hotkey.
|
||||
- Device/item UIs can be moved around by dragging.
|
||||
- Allow using devices while on a ladder or sitting on a chair.
|
||||
- Changed reactor temperature bar colors (from blue to red).
|
||||
- Higher quality stun batons cause heavier stun.
|
||||
- Changed unit load device capacity to 12 (because the sprite has space for 12) and made them waterproof.
|
||||
- Changed fabricator skill calculations: the most inadequate of the required skills determines the fabrication time (instead of the average).
|
||||
- Made dying drop a characters' skills towards the maximum initial skill instead of minimum.
|
||||
- Added a new keybind for opening and closing the chat box. The default bind is B.
|
||||
- Added a warning if a new keybind overlaps with any of the player's existing binds.
|
||||
- Overvoltage makes devices perform better, increasing the output of engines, making fabricators, deconstructors and pumps operate faster, electrical discharge coils do more damage, batteries recharge faster and oxygen generators generate more oxygen. Encourages operating the reactor manually and hopefully makes it a little more engaging.
|
||||
- Added more randomness to junction box overvoltage damage, and made partially damaged boxes take more damage from overvoltage. Prevents all boxes from breaking at the same time, making overvoltage less of a pain to deal with and intentionally overvolting devices more worthwhile.
|
||||
- Added manual temperature adjustment buttons which immediately increase/decrease the temperature of the reactor for a brief amount of time on manual control (bumps the gauge up/down by a fifth, and the boost fades out in 20 seconds). Allows reacting to load fluctuations very quickly, and conserving fuel by operating the reactor at a lower fission rate – a new benefit to operating reactors manually.
|
||||
- Signals no longer set the fission and turbine rates of the reactor instantaneously, making automated reactor circuits less overpowered. They are still viable, but especially now with the addition of the extra incentives for operating the reactor manually, they're no longer as clearly the best and most efficient way to operate the reactor, making manual operation more worthwhile.
|
||||
- Made the "distort" camera effect a little less obtrusive and glitchy-looking (smoother texture + less heavy effect).
|
||||
- Made water-sensitive materials (lithium, potassium, sodium) spawn in waterproof chemical crates.
|
||||
- Made crates deconstruct much faster to make them easier to get rid of.
|
||||
- Sonar disruptions hide minerals.
|
||||
- Gray out RangedWeapon's crosshair when reloading (similar to turret crosshairs).
|
||||
- Sonar disruptions now hide minerals.
|
||||
- Grayed out ranged weapons' crosshair when reloading (similar to turret crosshairs).
|
||||
- Disabled the autodocking prompt (which verifies whether you actually want to dock when docking is initiated by an automated circuit) in single player.
|
||||
- Improved the way drag is applied on submerged items. Fixes heavy items dropping at unnaturally high speeds in water.
|
||||
- Added a splash effect when an item falls into water.
|
||||
- The deconstructor UI shows what the input items deconstruct to (particularly important now with the lossy deconstruction recipes - it can be risky to deconstruct something just to see what materials it gives out if that results in material loss).
|
||||
- Wall and device repair costs in outposts are calculated based on the amount of damage on your sub, instead of always having a fixed price.
|
||||
- Inflamed lung doesn't affect characters that don't need oxygen.
|
||||
- Added swarm behavior for crawler husks.
|
||||
- Added some more oomph to nuclear explosions.
|
||||
- Adjust the alpha of the outpost service icons according to distance to make it easier to estimate where the NPC is at. Show the title of the NPC when hovering the cursor over the icon.
|
||||
- Added "unlockmission" console command.
|
||||
- Added "setcampaignmetadata" console command (may be useful for modders creating custom scripted events for the campaign).
|
||||
- Changed how NPC "titles" work. Previously we defined "titles" for the pirates (e.g. "Pirate Lord" and such), and the title replaced the name of the NPC (which made their dialog a little awkward). Now we display both the name and the title over the character, and special outpost NPCs also have titles.
|
||||
- Gave diving masks to most NPCs.
|
||||
- Changed the burn overlay formula: now also the non-affected limbs get half of the effect, because the sharp contrast between limbs looked weird.
|
||||
- Restored the 3-shell Railgun rack as a legacy option.
|
||||
- Reworded the "respawn with penalty" prompt to make it less confusing: you always get a penalty to your skills when you die now, and Reaper's Tax is an "extra penalty" you get on top of that if you opt to respawn mid-round. The intention behind this is to incur a cost to respawning, as it shouldn't be possible to get unlimited free reinforcements and supplies mid-round.
|
||||
- Made SIGTERM close the linux server gracefully.
|
||||
- Made respawn items (suits, scooters) spawn in the respawn shuttle's cabinets when possible.
|
||||
- Show a healthbar on items (e.g. eggs and thalamus organs) when damaging them with handheld weapons (melee or ranged).
|
||||
|
||||
Fixes:
|
||||
- Fixed clients downloading submarines they already have from the server if the mods those submarines are in are not currently enabled.
|
||||
- Fixed submarines always saving in the root folder of a local mod, instead of the subfolder they were originally in.
|
||||
- Fixed Reaper's Tax not stacking.
|
||||
- Fixed turrets linked to the same loader messing up the upgrade store UI and causing item swaps to cost more than they should.
|
||||
- Fixed status monitor calculating linked hulls' water amounts incorrectly (displaying the average of their water percentages, which isn't correct if the hulls aren't the same size).
|
||||
- Fixes to messed up ruin decals in a bunch of ruin modules.
|
||||
- Fixed a waypoint issue in the Alien_Entrance3 ruin module.
|
||||
- Removed oxygen tanks from DockingModule_01_Colony.
|
||||
Submarines:
|
||||
- Added a new intermediate transport sub, Camel.
|
||||
- Added submarine tiers. Higher-tier submarines can be upgraded further than lower-tier submarines.
|
||||
- Overhauled and balanced submarine upgrades.
|
||||
- Added Large Weapon Hardpoints.
|
||||
- Added Flak Cannon and Double Coilgun as new Large Weapons.
|
||||
- Railgun is now considered a Large Weapon.
|
||||
- Added an upgrade that adds a mineral scanner to nav terminals and sonar monitors.cannon
|
||||
- Submarine class now affects which upgrades are available for the sub.
|
||||
- Removed the Deep Diver class: the way we see it, Deep Divers didn't have a clear enough role in the game, especially considering that hull upgrades served pretty much the same purpose. In practice, the only clear benefit of a Deep Diver was being able to get through the very last levels of the campaign, and having to switch to one just for that purpose wasn't fun. Now any submarine with full hull upgrades can get all the way to the end of the campaign.
|
||||
- Fixed messy wiring in Typhon 2's bottom left hardpoint.
|
||||
- Winterhalter and Remora are now Scout class ships.
|
||||
- Added some loose vents and panels to Herja, Winterhalter and Barsuk, fixed invisible "loose panel" (news stand) in Orca 2.
|
||||
- Fixed floating light component in Orca 2.
|
||||
- Medical fabricator now consumes 500 power on all submarines, to be consistent with other fabricators.
|
||||
- Updated prices of all submarines to match tiers.
|
||||
- Gave Typhon 2 better stats and even more firepower, to outclass the original Typhon.
|
||||
- Improved R-29's speed and gave it a Flak Cannon.
|
||||
- Added Large Weapon hardpoints to Berilia to make it a Tier 3 transport.
|
||||
- Tweaked the hulls and waypoints around Herja's top docking hatch to make it easier for bots to reach and weld.
|
||||
- Fixed a waypoint/hull issue in Typhon's stowage compartment (waypoint in such a tight space the bots couldn't reach it).
|
||||
- Fixed inactive components (components not currently sending any signal) not reactivating if their output is set to a non-empty value.
|
||||
- Fixed duct block's misaligned broken sprite.
|
||||
|
||||
Balance:
|
||||
- "Mission cheesing" by repeatedly undocking and redocking to an outpost to reroll the mission events no longer works: new mission events don't reappear until one "world step" has passed (~10 minutes or traversing through one level).
|
||||
- Balance pass on handheld weapons: adjusted reload times, damages, stun durations, recoil and ammo stack sizes.
|
||||
- Reduced tools' structure damage (dual-wielded storage containers no longer chew through submarine walls in seconds).
|
||||
- Increased heavy ruin wall health to make it less easy to cheese your way into the artifact room in ruins.
|
||||
- Made boomstick fire in bursts of 2 (similar to deadeye carbide) to prevent ridiculous fire rates with quick-reloading.
|
||||
- Added EMP effect to nuclear depth charges for consistency.
|
||||
- Pulse Laser and Railgun now have similar power consumption as other turrets.
|
||||
- Changed how skill levels affect the quality of fabricated items. Previously having a skill level equal to or higher than the item's skill requirement would result in a good quality item, meaning that practically everyone could e.g. fabricate good quality oxygen tanks. Now your skill needs to be >20% from the minimum skill requirement towards 100 (e.g. if the item requires 20 skill to fabricate, 36 results in a higher quality item).
|
||||
- Reduced PUCS's radiation resistance from 100% to 90%. Complete invulnerability to radiation has way too much potential for exploits and overpowered strategies.
|
||||
- Adjusted supplies in pirate submarines.
|
||||
- Turned some weapons' burn damage into explosion damage.
|
||||
- Made the extra sales from "traveling tradesman" talent stack.
|
||||
- Terminal ignores empty signals.
|
||||
- Reduced commonness of molochs (as they can take a lot of time to kill, running into multiple of them can quickly become a chore)
|
||||
- Removed steel requirement for depth charges. Fabricate decoy depth charges from depth charges, rather than from the base material.
|
||||
- Reduced the Pulse Laser tri-laser bolt spread.
|
||||
- Explosions are now calculated differently, using the number of limbs to divide the damage (up to a maximum of 15 limbs). Adjusted explosion damage values to match new calculations.
|
||||
- Coilgun costs 5000 marks to install, Pulse Laser and Chaingun 6000. Large turrets each cost 7500 each.
|
||||
- Made mudraptor eggs modestly profitable for farming (decreased cost from shop, increased deconstruction yields).
|
||||
- Mineral yield and spawn rates rebalance: minerals found are now much more dependent on location (biome, cave, abyss).
|
||||
- Balanced existing mineral missions: adjusted rewards & required minerals, and required some minerals to be handed over to the outposts as proof of their existence.
|
||||
- Rebalanced Engine Force values to better match hull size. Most Scouts (Azimuth, Orca2, Remora, Winterhalter) are now faster. Humpback, Typhon and Orca are slightly slower.
|
||||
|
||||
Multiplayer:
|
||||
- Fixed missions sometimes unlocking in incorrect locations in MP campaign, making them either unselectable or causing a "mission mismatch" error when the round starts.
|
||||
- Fixed clients downloading submarines they already have from the server if the mods those submarines are in are not currently enabled.
|
||||
- Significantly sped up file transfers (mods, submarine files, campaign saves).
|
||||
- Clients who've recently joined (by default 2 minutes) are not allowed to vote to kick others, and vote kicking someone always requires at least 2 votes.
|
||||
- Servers don't allow selecting hidden jobs (jobs only used by NPCs) as job preferences.
|
||||
- The minimum kick vote counts are no longer rounded down. Previously if you had for example four players on the server and the minimum vote count set to 60%, kicking would require 2 votes, now it requires 3.
|
||||
- Fixed inventory and wallet resetting if a campaign round ends when a client's character has spawned, but the client is not currently controlling it (e.g. due to getting kicked to the lobby).
|
||||
- Fixed spectator checkbox overlapping with the character info if you get kicked to the lobby mid-round.
|
||||
- Fixed "kick" button staying disabled indefinitely if you vote to kick someone and the vote doesn't go through.
|
||||
- Fixed Steamworks publish tab showing the "free weekend" message when using Steam family sharing.
|
||||
- Minor tweaks to the end of PvP missions to make them a little less underwhelming: instead of ending the round immediately when one team is dead (without even giving enough time to see the enemy die), there's a brief delay, a message box and a camera transition to let the players see what happened.
|
||||
- Fixed PvP team assignment sometimes being wildly imbalanced, even when there were enough players with no preference to make the team sizes equal.
|
||||
- Fixed clients getting stuck in the loading screen if they happen to disconnect at the right moment between rounds.
|
||||
- Fixed bank balance not getting corrected if it's become desynced by e.g. client-side commands.
|
||||
- Fixed server not registering a client's character as disconnected if the client disconnects and reconnects before the round has fully started, causing the client to get stuck as a spectator when they rejoin.
|
||||
- Fixed clients disabling their client-side-only mods when they join a server.
|
||||
- Fixed hull/item repairs purchased from an outpost sometimes not getting applied client-side.
|
||||
- Fixed "missing entity" errors in a specific situation in multiplayer. Occurred when a respawn shuttle was enabled and loaded on the server (= i.e. in a non-outpost level), and a client disconnected and immediately reconnected. This would cause the client to deselect the respawn shuttle and make them start the round without loading one, leading to the "missing entity" issues due to the shuttle only existing server-side.
|
||||
- Fixed damage visuals not showing on characters who've died off-screen.
|
||||
- Fixed ability to upgrade the sub when there's a switch pending in multiplayer.
|
||||
- Fixed friendly fire and karma always showing up as disabled on dedicated servers in the server list.
|
||||
- Fixed spineling spikes fired by a human with spineling genes not damaging any human characters (enemies in PvP, pirates in pirate missions) when friendly fire is disabled.
|
||||
- Fixed "invalid ExecuteAttack message: limb index out of bounds" errors when you join a server where a character has fired spineling spikes with spineling genes mid-round.
|
||||
- Fixed "entity not found" errors if a shuttle or submarine ends up absurdly deep in multiplayer (>100 km). We don't even know how someone managed to pull this off.
|
||||
- Fixed rapidly clicking on the mission giver sometimes not giving all the available missions when the "Use" input is set to LMB. Happened because the conversation logic didn't check if there's another conversation active, causing the server to show a new conversation when clicking the NPC, without interrupting/continuing the previous conversation.
|
||||
- Made shockjock event only show for the player triggering the event (making it visible for everyone works kind of weirdly, when the event involves talking to an NPC next to the character who triggered the event).
|
||||
- Fixed outpost events getting stuck at the last ConversationAction if another client has finished the action.
|
||||
|
||||
Optimization:
|
||||
- Updated our runtime to .NET 6, which should yield significant performance improvements. Do note that this unfortunately means we'll have to drop support for macOS versions older than 10.15, but we have taken some measures to help the affected Mac players continue having access to Barotrauma. More info here https://store.steampowered.com/news/app/602960/view/3367025204056277713.
|
||||
- Optimized afflictions that apply other afflictions on the character (e.g. radiation sickness, drunkenness, opiate withdrawal).
|
||||
- Optimizations to the talent system, particularly when the talent menu is open and when there's a large number of talents (e.g. when using mods that make all talents available to every class).
|
||||
- Physics optimization: fixed submerged items' physics bodies staying active indefinitely even after they've come to rest due to buoyant forces being applied on them constantly. Now we stop updating bodies that have come to rest on the floor and aren't light enough to float.
|
||||
- Optimized AI objectives that make bots fetch items (combat, contain item, decontain item, get item).
|
||||
|
||||
Submarine editor:
|
||||
- Fixed door gaps not appearing in the sub editor until you select the door
|
||||
|
||||
Modding:
|
||||
- Added CheckTalentAction, which can be used in events to check whether a target has unlocked a specific talent.
|
||||
- Fixed ExtraLoad working the wrong way around on PowerTransfer components that generate/consume power (the extra load would supply power to the grid). Does not affect the vanilla game, because neither junction boxes or relays generate or consume power.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.4.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Fixed networking issues that prevented joining SteamP2P servers.
|
||||
- Fixed crashing on startup if GameAnalytics is enabled.
|
||||
- Fixed sub editor background images not saving if you leave the editor and quit the game without exiting the image editing mode first.
|
||||
- Camel hull fixes.
|
||||
- Fixed a draw order issue in unit load device.
|
||||
- Fixed unit load device's inventory slot layout.
|
||||
- Fixed submarine tier set in the sub editor not being saved, causing all subs to use the default tier determined by the price.
|
||||
|
||||
Changes:
|
||||
- Made extra sales from "traveling tradesman" talent stack.
|
||||
- Fixed door gaps not appearing in the sub editor until you select the door.
|
||||
- Fixed sub editor background images not saving.
|
||||
- Fixed turret lightsource rotation not refreshing in the sub editor when flipping the item.
|
||||
- Fixed prefab placement breaking in the sub editor if LMB is held while moving the cursor outside of the selection panel.
|
||||
- Fixed several instances of janky UI interactions in the submarine editor: dragging the selection rectangle now works even if the cursor reaches into the prefab list; letting go of a dragged entity works even if the cursor reaches into the prefab list; the dragged entity no longer goes invisible when reaching into the prefab list.
|
||||
- Made PowerContainer recharge speed always default to 0.
|
||||
- Fixed adding resizeable items (like ladders) not being registered in the sub editor's command history, preventing undoing it.
|
||||
- Changed default reactor output from 10,000 kW to 5000 kW.
|
||||
- Decreased Winterhalter reactor output and increased its fuel consumption rate.
|
||||
- Fixed some gap issues in Winterhalter.
|
||||
- Fixed medics not having access to the toxin cabinet in Barsuk.
|
||||
- Fixed medic, engineer and mechanic spawnpoints having no tags in Typhon.
|
||||
- Fixed crashing when trying to multi-edit a string value in the sub editor.
|
||||
- Fixed dragged objects becoming invisible if you bring the cursor over a UI element in the sub editor.
|
||||
- Fixed screwdrivers and wires in your "inventory" being included in the total item count in the sub editor's wiring mode.
|
||||
- Fixed entities that were below the cursor when starting to resize a structure staying highlighted during resizing.
|
||||
- Fixed sub editor treating the autosave interval as minutes instead of seconds (saving every 300 minutes instead of 300 seconds).
|
||||
|
||||
Fixes:
|
||||
- Fixed "power flowback" issue in turrets. As of the power rework, wires connected to the same input or output pin of a device are considered to be in the same grid, which in practice meant a turret could be connected to another supercapacitor through the power_in connection of another turret, even if there was no direct connection between the 1st turret and the supercapacitor. Now the turrets (and electrical discharge coils) need to be wired directly to the supercapacitor.
|
||||
- Fixed brief freezes when monsters spawn mid-round.
|
||||
- Fixed turrets linked to the same loader messing up the upgrade store UI and causing item swaps to cost more than they should.
|
||||
- Fixed submarines always saving in the root folder of a local mod, instead of the subfolder they were originally in.
|
||||
- Fixed Reaper's Tax not stacking.
|
||||
- Fixes to ruin decals in a bunch of ruin modules.
|
||||
- Fixed a waypoint issue in the Alien_Entrance3 ruin module.
|
||||
- Removed oxygen tanks from DockingModule_01_Colony.
|
||||
- Fixed duct block's misaligned broken sprite.
|
||||
- Fixed status monitor calculating linked hulls' water amounts incorrectly (displaying the average of their water percentages, which isn't correct if the hulls aren't the same size).
|
||||
- Fixed inactive components (components not currently sending any signal) not reactivating if their output is set to a non-empty value.
|
||||
- Fixed missing gap in SecurityModule_02.
|
||||
- Fixed lack of outpost events in difficulties past 80 (which no longer occur normally but still exist in old saves and mods).
|
||||
- Fixed lithium and magnesium descriptions.
|
||||
- Adjusted hulls in DockingModule_02_Colony to prevent bots from jumping off the ledge.
|
||||
- Fixed motion sensors detecting pets as monsters (pets are now a separate target type).
|
||||
- Fixed helmets not protecting against concussions.
|
||||
- Fixed safety harness not protecting against lacerations.
|
||||
- Fixed increasing an item's HealthMultiplier not increasing the current condition (so e.g. doubling the item's max health would cause it to have 50% condition).
|
||||
- Fixed successive event dialogs in the same prompt scrolling the prompt back up and then down.
|
||||
- Fixed missing "pirateclothes" inventory icon.
|
||||
- Made bots better at figuring out which button controls a door when there's some complex circuit involved. Previously the bots would try to find a button connected to any of the door's connections via wires/circuits, now only the toggle and set_state inputs are considered.
|
||||
- Bots now heavily prefer using buttons linked to the door in the sub editor. Can be used as another way to help the bots figure out which button they should press in situations with multiple buttons and complex door control logic.
|
||||
- Fixed bots failing to find a path to a couple of spots in Herja.
|
||||
- Bots now clearly prefer using buttons linked to the door in the sub editor. Can be used as another way to help bots figure out which button they should press in situations with multiple buttons and complex door control logic.
|
||||
- Fixed bots failing to find a path to a couple of spots on Herja.
|
||||
- Fixed alien materials (physicorium, incendium, fulgurium, dementonite, paralyxis) not being shown on the mineral scanner.
|
||||
- Another fix to cave generation to prevent it from creating impassable paths.
|
||||
- Fixed inability to use manual assignment for bot orders with options.
|
||||
- Fixed all boolean components (And, Or, Xor) using the And Component's tooltip for the "timeframe" property.
|
||||
- Fixed boolean operator component (And, Or, Xor) timeframes not working correctly in some situations (non-zero timeframe, empty false output). The component would deactivate as soon as it stops sending an output, which could prevent some inputs from timing out (meaning that the component could send a signal again as soon as it receives signal A, even if signal B hasn't been received within the timeframe).
|
||||
- Fixed PUCS consuming the medical item inside it when a welding fuel or incendium tank is inserted.
|
||||
|
||||
Multiplayer:
|
||||
- Fixed inventory and wallet resetting if a campaign round ends when a client's character has spawned, but the client is not currently controlling it (e.g. due to getting kicked to the lobby).
|
||||
- Fixed spectator checkbox overlapping with the character info if you get kicked to the lobby mid-round.
|
||||
|
||||
Modding:
|
||||
- Fixed items/structures now falling back to the description defined in the xml even if it's empty, if the description is not defined for the selected language (instead of using English instead).
|
||||
- Fixed using the "reloadwearables" and "loadwearable" console commands outside the character editor crashing the game.
|
||||
- Fixed character editor crash if you first reload textures and then recreate the ragdoll.
|
||||
- Changed how submarine upgrades are calculated, now no longer adds previous levels' costs to the price, but rather relies on higher increasehigh values
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.3.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Fixed deconstructors and research stations showing what unidentified genetic materials are.
|
||||
- Fixed fabricator not showing the number of required items on recipes.
|
||||
- Fixed a crash in Fabriactor.DrawInputOverLay.
|
||||
- Visual improvements to draggable item UIs.
|
||||
- Fixed reactor sliders adjusting to the received signals until the received value has been matched, leading to buggy-looking behavior when the sliders adjust by themselves after the signal wires have been disconnected. Now the reactor stops following the signals if nothing is received in 1 second.
|
||||
- Misc fixes and improvements to Camel.
|
||||
- Fixed vote-kicked players being unable to rejoin when they're unbanned.
|
||||
|
||||
Changes:
|
||||
- Updated our runtime to .NET 6, which should yield significant performance improvements. Do note that this unfortunately means we'll have to drop support for macOS versions older than 10.15, but we have taken some measures to help the affected Mac players continue having access to Barotrauma. More info here https://store.steampowered.com/news/app/602960/view/3367025204056277713.
|
||||
- First version of reworked tutorials. Still very much a work in progress (only the 1st tutorial that teaches you the very basics is close to done), but would still appreciate feedback!
|
||||
- Changed unit load device capacity to 12 (because the sprite has space for 12) and made them waterproof.
|
||||
- Changed fabricator skill calculations: the most inadequate of the required skills determines the fabrication time (instead of the average).
|
||||
- Made dying drop a characters' skills towards the maximum initial skill instead of minimum.
|
||||
- Added a new keybind for opening and closing the chat box. The default bind is B.
|
||||
- Added a warning if a new keybind overlaps with any of player's existing binds.
|
||||
- Balanced existing mineral missions: adjusted rewards & required minerals and required some minerals to be handed over to the outposts as proof of their existence.
|
||||
- Added new mining missions, including some in the abyss.
|
||||
|
||||
Submarines:
|
||||
- Added submarine tiers. Higher-tier submarines can be upgraded futher than lower-tier submarines.
|
||||
- Overhauled and balanced submarine upgrades.
|
||||
- Added an upgrade that adds a mineral scanner to nav terminals and sonar monitors.
|
||||
- Submarine class now affects which upgrades are available for the sub.
|
||||
- Removed the Deep Diver class: the way we see it, Deep Divers didn't have a clear enough role in the game, especially considering that hull upgrades served pretty much the same purpose. In practice, the only clear benefit of a Deep Diver was being able to get through the very last levels of the campaign, and having to switch to one just for that purpose wasn't fun. Now any submarine with full hull upgrades can get all the way to the end of the campaign.
|
||||
|
||||
Fixes:
|
||||
- Fixed a level generation issue that sometimes made the level impassable if there happened to be a cave right above the outpost.
|
||||
- Fixed holes on sloped walls being impossible to pass through when you're swimming straight down/up (or straight right/left depending on the wall): the walls are technically considered either horizontal or vertical (depending on the angle of the slope), and you would have to swim in a direction perpendicular to this "technical" direction of the wall.
|
||||
- Fixed retrying the Hognose mission making a new Hognose join your crew every time.
|
||||
- Fixed idling NPCs sometimes getting stuck on ladders.
|
||||
- Fixed mirrored turrets being displayed backwards on status monitor.
|
||||
- Fixed mirrored turrets being displayed backwards on the status monitor.
|
||||
- Fixed character's hands getting "stuck" if you handcuff yourself while dragging someone.
|
||||
- Fixed dragged character's arms not being pulled towards you, making it look like you're dragging them without touching if you run or walk away while dragging.
|
||||
- Fixed dragged bots slowly moving constantly, preventing them from switching to the normal standing pose.
|
||||
- Fixed bots having trouble fixing leaks in multi-hull rooms: they were required to be in the same hull as the leak, which prevented them from fixing leaks in e.g. R-29's bilge.
|
||||
- Fixed combat missions not ending the round if both crews are dead.
|
||||
- Fixed bots stating the name of the character they're firing at with turrets, making it seem like they know the name of every pirate they come across and magically recognize them through the walls of the enemy sub.
|
||||
- Fixed chaingun rotation speed not being affected by the weapons skill.
|
||||
- Fixed Chaingun rotation speed not being affected by the weapons skill.
|
||||
- Fixed crashing when using ':' in item assembly names on Linux platforms.
|
||||
- Fixed ImmuneToPressure ability flag being ignored on characters who don't need air (in practice meaning that you can get killed by pressure if you get huskified even if you have a talent that makes you immune to pressure).
|
||||
- Fixed geneticmaterialcrawler_unresearched3 producing mudraptor genes.
|
||||
|
||||
Submarine editor:
|
||||
- Fixed sub editor background images not saving.
|
||||
|
||||
Optimization:
|
||||
- Optimized affliction that apply other afflictions on the character (e.g. radiation sickness, drunkenness, opiate withdrawal).
|
||||
- Physics optimization: fixed submerged items' physics bodies staying active indefinitely even after they've come to rest due to buoyant forces being applied on them constantly. Now we stop updating bodies that have come to rest on the floor and aren't light enough to float.
|
||||
- Optimized AI objectives that make the bots fetch items (combat, contain item, decontain item, get item).
|
||||
|
||||
Multiplayer:
|
||||
- Fixed "kick" button staying disabled indefinitely if you vote to kick someone and the vote doesn't go through.
|
||||
- Fixed Steamworks publish tab showing the "free weekend" message when using Steam family sharing.
|
||||
|
||||
Modding:
|
||||
- Fixed inability to localize item names if the name is defined directly in the item config.
|
||||
- Allowed defining where mineral mission resources are spawned using the "positiontype" attribute. The supported types are "MainPath", "SidePath", "Cave", and "AbyssCave".
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.2.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Fixed an item getting placed in the sub editor when you select one from the entity list.
|
||||
- Made flak cannon ammo proximity-triggered.
|
||||
- Made flak cannon's sounds and visual effects more powerful.
|
||||
- Fixed LightComponents turning themselves on when the round starts.
|
||||
- Fixed "Input string was not in a correct format" when loading favorite/recent servers with an empty QueryPort.
|
||||
- Fixed deconstructor showing contained items in the deconstruction output.
|
||||
- Fixed double coilgun sometimes not playing a sound on every shot.
|
||||
- Change the cursor to a hand on draggable item UIs.
|
||||
- Fixed crashing during level generation if abyss resources have not been configured.
|
||||
- Fixed hull & item repairs (and presumably replacing lost shuttles) not working in single player unless you save and reload.
|
||||
- Visual improvements to Camel.
|
||||
- Fixed timer that prevents recently joined players from voting to kick working the wrong way around.
|
||||
- Fixed inability to resize a resizeable structure when placing it in the sub editor.
|
||||
|
||||
Changes:
|
||||
- Reintroduced separate local/radio voice chat keys as a legacy option. Now it's again possible to speak with voice activation by default and use a push-to-talk button for radio, the same way as before, by setting the chat mode to Local and using the new radio voice chat hotkey.
|
||||
- Changed reactor temperature bar colors (from blue to red).
|
||||
- Higher quality stun batons cause heavier stun.
|
||||
- Disabled the autodocking prompt (which verifies whether you actually want to dock when docking is initiated by an automated circuit) in single player.
|
||||
- Minor tweaks to the end of PvP missions to make them a little less underwhelming: instead of ending the round immediately when one team is dead (without even giving enough time to see the enemy die), there's a bried delay, a message box and a camera transition to let the players see what happened.
|
||||
|
||||
Submarines:
|
||||
- Fixed floating light component in Orca 2.
|
||||
- Medical fabricator now consumes 500 power on all submarines, to be consistent with other fabricators
|
||||
- Rebalanced Engine Force values to better match hull size. Most Scouts (Azimuth, Orca2, Remora, Winterhalter) are now faster. Humpback, Typhon and Orca slightly slower.
|
||||
- Winterhalter and Remora are now Scout class ships, Deep Diver class will be removed.
|
||||
- Introduced submarine tiers. Submarine tiers and class affect the max level of submarine upgrades that apply / can be bought.
|
||||
- Updated prices of all submarines to match tiers.
|
||||
- Gave Typhon 2 better stats and even more firepower, to outclass the original Typhon.
|
||||
- Improved R-29 speed and gave a Flak cannon
|
||||
- Added Large weapon hardpoints to Berilia to make it a Tier 3 transport.
|
||||
|
||||
Optimization:
|
||||
- Optimizations to the talent system, particularly when the talent menu is open and when there's a large number of talents (e.g. when using mods that make all talents available to every class).
|
||||
|
||||
Multiplayer:
|
||||
- Significantly sped up file transfers (mods, submarine files, campaign saves).
|
||||
- The minimum kick vote counts are no longer rounded down. Previously if you had for example four players on the server and the minimum vote count set to 60%, kicking would require 2 votes, now it requires 3.
|
||||
|
||||
Submarine editor:
|
||||
- Fixed turret lightsource rotation not refreshing in the sub editor when flipping the item.
|
||||
- Fixed turret lightsource rotation not refreshing in the sub editor when flipping the item.
|
||||
|
||||
Fixes:
|
||||
- Fixed linked subs still sometimes getting placed on the wrong side of the docking port when switching subs.
|
||||
- Fixed PvP team assignment sometimes being wildly imbalanced, even if there's enough players with no preference to make the team sizes equal.
|
||||
- Fixes to ruin door connections, wiring and connection panels.
|
||||
- Fixed "insurance policy" giving the money to the dead character instead of the bank.
|
||||
- Fixed damage to mirrored wall pieces resetting between rounds.
|
||||
- Increased the minimum width of cave tunnels to prevent impassable paths.
|
||||
- Fixed deconstructor input slots becoming unlocked when starting a new round while the deconstructor is running.
|
||||
|
||||
Modding:
|
||||
- Fixed console errors when trying to check int values with PropertyConditionals.
|
||||
- Fixed melee weapon's StrikingPowerMultiplier only affecting the afflictions defined in the Attack, not ones defined in the status effect.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.1.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Option to lock or reset draggable item UIs.
|
||||
- Force item UI layout update when resolution changes. Fixes item repositioned item UIs potentially getting left outside the window when switching to a smaller resolution.
|
||||
- Item UIs can't be dragged under each other.
|
||||
- Fixed dragging a wire in a connection panel dragging the panel too if you bring the cursor close to the edges of the panel.
|
||||
- Fixed seated bots being unable to get up from the chair.
|
||||
- Fixed some more unwired lights in ResearchModule_02_Colony.
|
||||
- Fixed another minor gap issue in Winterhalter.
|
||||
- Fixed hull/item repairs and the money spent on them appearing to reset if you join mid-round after repairs have been purchased.
|
||||
- Fixed light source staying in place after detaching a glowing item (e.g. mineral) from a wall.
|
||||
- Fixed server trying to place all previously spawned respawn items into containers on every respawn, even if the items have already been removed. Happened because we never cleared respawnItems, and because we used that list when placing new respawn items into containers.
|
||||
|
||||
Changes and additions:
|
||||
- Overvoltage makes devices perform better, increasing the output of engines, making fabricators, deconstructors and pumps operate faster, electrical discharge coils do more damage, batteries recharge faster and oxygen generators generate more oxygen. Incentivizes operating the reactor manually and hopefully makes it a little more engaging.
|
||||
- Added more randomness to junction box overvoltage damage, and made partially damaged boxes take more damage from overvoltage. Prevents all boxes from breaking at the same time, making overvoltage less of a pain in the ass to deal with and intentionally overvolting the devices more worthwhile.
|
||||
- Experimental: Added a way to immediately increase/decrease the temperature of the reactor for a brief amount of time (atm bumps the gauge up/down by a fifth, and the boost fades out in 20 seconds). Allows reacting to load fluctuations very quickly, and to conserve fuel by operating the reactor at a lower fission rate = serves as another incentive to operate it manually.
|
||||
- Signals no longer set the fission and turbine rates of the reactor instantaneously, making automated reactor circuits less overpowered. They are still viable, but especially now with the addition of the extra incentives for operating the reactor manually, they're no longer as clearly the best and most efficient way to operate the reactor, making manual operation more worthwhile.
|
||||
- Added a Large Weapon Hardpoint.
|
||||
- Railgun is now considered a large weapon.
|
||||
- Added Flak Cannon and Double Coilgun as large weapons.
|
||||
- Pulse Laser and Railgun now have similar power consumption as other turrets.
|
||||
- Improved the way drag is applied on submerged items. Fixes heavy items dropping at unnaturally high speeds in water.
|
||||
- Added a splash effect when an item falls into water.
|
||||
|
||||
Balance:
|
||||
- Reduced the pulse laser tri-laser bolt spread.
|
||||
- Explosions are now calculated differently, using the number of limbs to divide the damage (to a max of 15 limbs). Adjusted explosion damage values to match new calculations.
|
||||
- Coilgun costs 5000 to install, Pulse Laser and Chaingun 6000. Large turrets all cost 7500.
|
||||
- Make mudraptor eggs slightly viable for farming (decreased cost from shop, increased deconstruction yields)
|
||||
- Mineral yield and spawn rates rebalance, minerals found are now much more dependant on location (biome, cave, abyss)
|
||||
|
||||
Submarines:
|
||||
- Added a new intermediate transport sub, Camel. Still WIP, but feedback is welcome.
|
||||
- Fixed messy wiring in Typhon 2's bottom left hardpoint.
|
||||
- Added some loose vents and panels to Herja, Winterhalter and Barsuk, fixed invisible "loose panel" (news stand) in Orca 2.
|
||||
|
||||
Fixes:
|
||||
- Fixed brief freezes when monsters spawn mid-round.
|
||||
- Fixed Grenade Launcher quality doing basically nothing, because it increased the minuscule amount of blunt force trauma the grenade causes on impact instead of the explosion damage.
|
||||
- Fixed vitality modifiers not being taken into account in the readings in the health interface. For example, gunshot wounds on the head cause a x2 larger vitality drop than on other limbs, but this wasn't displayed on the health interface.
|
||||
- Fixed Planet Neon Sign sprite bleed.
|
||||
- Fixed Grenade Launcher quality doing basically nothing, because it increased the minuscule amount of blunt force trauma the grenade causes on impact instead of the explosion damage.
|
||||
- Fixed level resource spawn rate not properly respecting level generations parameters' resource spawn chance values.
|
||||
- Fixed level resource spawn rate not properly respecting the resource spawn chance values of level generation parameters.
|
||||
- Fixed some text overflows in the hiring menu when using a small HUD scale.
|
||||
- Fixed name on an ID card resetting to the original name if you rename a character and then start a new round.
|
||||
- Fixed handcuffs in the backmost hand being drawn in front of the character.
|
||||
- Fixed water splashes appearing in an incorrect hull when a character's limb moves from a flooded hull to another hull, where the limb is no longer underwater.
|
||||
- Fixed crashing when a signal causes a wired item to get dropped (e.g. when you attach a detonator to a destructible ice wall and blow it up).
|
||||
- Fixed crashing when a signal causes a wired item to be dropped (e.g. when you attach a detonator to a destructible ice wall and blow it up).
|
||||
- Oxygen generators and shelves don't fill up oxygen tanks when on fire. Caused repeated explosions when the tank constantly refilled and re-exploded.
|
||||
- Fixed "gene harvester" and "deep sea slayer" working on all enemies, not just monsters.
|
||||
- Fixed floating point inaccuracy sometimes preventing items from being used as fabrication ingredients (e.g. oxygen generator may sometimes only fill tanks up to something like 99.9998%, which prevented it from being used in recipes that require a full tank).
|
||||
- Fixed floating point inaccuracy sometimes preventing items from being used as fabrication ingredients (e.g. an oxygen generator may sometimes only fill tanks up to something like 99.9998%, which prevented it from being used in recipes that require a full tank).
|
||||
- Fixed item picking timer (e.g. detaching an item from a wall) ticking down when the game is paused.
|
||||
- Fixed outpost supply cabs missing the oxygen tank spawns.
|
||||
- Made the water current outside the levels start from the same point where monsters start heading towards the level, to make sure monsters can't escape too far from sub with a weak engine.
|
||||
- Fixed outpost supply cabinets missing the oxygen tank spawns.
|
||||
- Made the water current outside the levels start from the same point where monsters start heading towards the level, to make sure monsters can't escape too far from subs with a weak engine.
|
||||
- Fixed hardened diving knife recipe.
|
||||
- Fixed probability multiplier not being shown in wearable tooltip if the damage multiplier is 1.
|
||||
- Yet another attempt to prevent beacon missions from failing for apparently no reason: sonar monitors won't get damaged by water after the beacon's been activated.
|
||||
- Don't stop selecting text in a textbox if the cursor goes outside the box.
|
||||
|
||||
Multiplayer:
|
||||
- Fixed clients getting stuck in the loading screen if they happen to disconnect at the right moment between rounds.
|
||||
- Fixed bank balance not getting corrected if it's gotten desynced by e.g. client-side commands.
|
||||
- Fixed server not registering a client's character as disconnected if the client disconnects and reconnects before the round has fully started, causing the client to get stuck as a spectator when they rejoin.
|
||||
- Fixed clients disabling their client-side-only mods when they join a server.
|
||||
- Fixed hull/item repairs purchased from an outpost sometimes not getting applied client-side.
|
||||
|
||||
Submarine editor:
|
||||
- Fixed prefab placement breaking in the sub editor if LMB is held while moving the cursor outside of the selection panel.
|
||||
- Fixed several instances of janky UI interactions in the submarine editor: dragging selection rectangle now works even if cursor reaches into the prefab list, letting go of a dragged entity works even if cursor reaches into the prefab list, dragged entity no longer goes invisible when reaching into the prefab list.
|
||||
- Made PowerContainer recharge speed always default to 0.
|
||||
- Fixed adding resizeable items (like ladders) not being registered in sub editor's command history, preventing undoing it.
|
||||
|
||||
Modding:
|
||||
- Added DamageMultiplier and LaunchImpulse to Turret. LaunchImpulse is now defined on turrets instead of ammunition (total impulse is the sum of turret + ammunition).
|
||||
- Added SnapRopeOnNewAttack property to Attacks: allows characters to switch attacks without snapping ropes from previous attacks.
|
||||
- Added dividebylimbcount to Explosion, which determines whether the damage is spread out among limbs (if set to true).
|
||||
- UpgradeCategories with no upgrades in them are hidden from the upgrade menu (i.e. if you modify the upgrades so some of the vanilla categories no longer contain any upgrades, those categories won't be shown).
|
||||
- Fixed affliction names and descriptions being empty if they're not available in the selected language nor configured in the affliction xml directly.
|
||||
- Fix custom infected husks seeking for any husk infection targeting the matching species instead of checking that also the husk affliction prefab matches the husk.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.19.0.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Changes:
|
||||
- Device/item UIs can be moved around by dragging.
|
||||
- Allow using devices while on a ladder or sitting on a chair.
|
||||
- Readded the option to have separate push-to-talk binds for local and radio voice chat (by default not bind to anything).
|
||||
- The deconstructor UI shows what the input items deconstruct to (particularly important now with the lossy deconstruction recipes - it can be risky to deconstruct something just to see what materials it gives out if that results in material loss).
|
||||
- Wall and device repair costs in outposts are calculated based on the amount of damage in your sub, instead of always having a fixed price.
|
||||
- Inflamed lung doesn't affect characters that don't need oxygen.
|
||||
- Added swarm behavior for crawler husks.
|
||||
- Added some more oomph to nuclear explosions.
|
||||
- Adjust the alpha of the outpost service icons according to distance to make it easier to estimate where the NPC is at, show the title of the NPC when hovering the cursor over the icon.
|
||||
- Added "unlockmission" console command.
|
||||
- Added "setcampaignmetadata" console command (may be useful for modders creating custom scripted events for the campaign?).
|
||||
- Changed how NPC "titles" work. Previously we defined "titles" for the pirates (e.g. "Pirate Lord" and such), and the title replaced the name of the NPC (which made their dialog a little awkward). Now we display both the name and the title over the character, and special outpost NPCs also have titles.
|
||||
- Gave diving masks to most NPCs.
|
||||
- Changed the burn overlay formula: now also the non-affected limbs get half of the effect, because it looks weird if there's a sharp contrast between the limbs.
|
||||
- Restored the 3 shell railgun rack as a legacy option.
|
||||
- Reworded the "respawn with penalty" prompt to make it less confusing: you always get a penalty to your skills when you die now, and Reaper's Tax is an "extra penalty" you get on top of that if you opt to respawn mid-round. The intention behind this is to incur a cost to respawning: it shouldn't be possible to get unlimited free reinforcements and supplies mid-round.
|
||||
- Made SIGTERM close the linux server gracefully.
|
||||
- Made respawn items (suits, scooters) spawn in the respawn shuttle's cabinets when possible.
|
||||
- Show a healthbar on items (e.g. eggs and thalamus organs) when damaging them with handheld weapons (melee or ranged).
|
||||
|
||||
Balance:
|
||||
- "Mission cheesing" by repeatedly undocking and redocking to an outpost to reroll the mission events no longer works: new mission events don't reappear until one "world step" has passed (~10 minutes or traversing through one level).
|
||||
- Balance pass on handheld weapons: adjusted reload times, damages, stun durations, recoil and ammo stack sizes.
|
||||
- Reduced tools' structure damages (dual-wielded storage containers no longer chew through submarine walls in seconds).
|
||||
- Increased heavy ruin wall health to make it less easy to cheese your way in to the artifact room in ruins.
|
||||
- Made boomstick fire in bursts of 2 (similar to deadeye carbide) to prevent ridiculous fire rates with quick-reloading.
|
||||
- Added EMP effect to nuclear depth charges for consistency.
|
||||
- Changed how skill levels affect the quality of fabricated items. Previously having a skill level equal to or higher than the item's skill requirement would result in a good quality item, meaning that practically everyone could e.g. fabricate good quality oxygen tanks. Now your skill needs to be >20% from the minimum skill requirement towards 100, (e.g. if the item requires 20 skill to fabricate, 36 results in a higher quality item).
|
||||
- Reduced PUCS's radiation resistance from 100% to 90%. Complete invulnerability to radiation has way too much potential for exploits and overpowered strategies.
|
||||
- Adjusted supplies in pirate submarines.
|
||||
- Turn some weapons' burn damage into explosion damage (wip).
|
||||
- Terminal ignores empty signals.
|
||||
- Reduce commonness of Molochs (as they can take a lot of time to kill, running into multiple of them can quickly become a chore)
|
||||
- Remove steel requirement for depth charges. Fabricate decoy depth charges from depth charge, rather than base material.
|
||||
|
||||
Multiplayer:
|
||||
- Clients who've recently joined (by default 2 minutes) are not allowed to vote to kick others, and vote kicking someone always requires at least 2 votes.
|
||||
- Fixed "missing entity" errors in a specific situation in multiplayer. Occurred when a respawn shuttle was enabled and loaded on the server (= i.e. in a non-outpost level), and a client disconnected and immediately reconnected. This would cause the client to deselect the respawn shuttle, and make them start the round without loading one, leading to the "missing entity" issues due to the shuttle only existing server-side.
|
||||
- Fixes damage visuals not showing on characters who've died off-screen.
|
||||
- Servers doent allow selecting hidden jobs (jobs only used by NPCs) as job preferences.
|
||||
- Fixed ability to upgrade the sub when there's a switch pending in multiplayer.
|
||||
- Fixed friendly fire and karma always showing up as disabled on dedicated servers in the server list.
|
||||
- Fixed spineling spikes fired by a human with spineling genes not damaging any human characters (enemies in PvP, pirates in pirate missions) when friendly fire is disabled.
|
||||
- Fixed "invalid ExecuteAttack message: limb index out of bounds" errors when you join a server where a character has fired spineling spikes with spineling genes mid-round.
|
||||
- Fixed "entity not found" errors if a shuttle or submarine ends up absurdly deep in multiplayer (> 100 km). I don't even know how someone managed to pull this off.
|
||||
- Fixed rapidly clicking on the mission giver with the Use input set to LMB sometimes not giving all the available missions. Happened because the conversation logic didn't check if there's another conversation active, causing the server to show a new conversation when clicking the NPC, without interrupting/continuing the previous conversation.
|
||||
- Made shockjock event only show for the player triggering the event (making it visible for everyone works kind of weirdly, when the event involves talking to an NPC next to the character who triggered the event).
|
||||
- Fixed outpost events getting stuck at the last ConversationAction if another client has finished the action.
|
||||
|
||||
Submarines:
|
||||
- Changed default reactor output from 10,000 kW to 5000 kW.
|
||||
- Decreased Winterhalter reactor output, increased fuel consumption rate.
|
||||
- Fixed some gap issues in Winterhalter.
|
||||
- Fixed medics not having access to the toxin cabinet in Barsuk.
|
||||
- Fixed medic, engineer and mechanic spawnpoints having no tags in Typhon.
|
||||
|
||||
Fixes:
|
||||
- Fixed text selection in a textbox stopping when the cursor goes outside the box.
|
||||
- Fixed fire, breach and intruder report icons not being shown to anyone.
|
||||
- Fixed missing/unwired lighting in ResearchModule_02_Colony.
|
||||
- Remove particles when switching screens (otherwise e.g. particles from the previous round are still in the level if you happen to be looking at the right spot).
|
||||
- Thalamus or ice walls can't be welded.
|
||||
- Quick-reloading tries to reload the item whose contained items have the lowest condition. In other words, if you've equipped 2 weapons, quick-reloading reloads the one with the least ammo instead of the one that's the first in your inventory.
|
||||
- Fixed messed up dementonite and depleted fuel tool recipes.
|
||||
- Fixed erroneous dementonite and depleted fuel tool recipes.
|
||||
- Fixed swapping a scaled turret/hardpoint causing the new one to be misplaced.
|
||||
- Fixed inability to upgrade the sub or do maintenance if you buy and opt to switch to a new sub, and then go to the submarine switch terminal to cancel the switching.
|
||||
- Fixed stolen items becoming non-stolen when deconstructed.
|
||||
- Fixed ItemContainer UI popping up (with no visible inventory slot) when you pick one up. For example when you pick up a detonator from the floor.
|
||||
- Fixed ItemContainer UI popping up (with no visible inventory slot) when you pick one up, e.g. picking up a detonator from the floor.
|
||||
- Fixed "[E] Rewire" hover text being shown on attachable items that haven't been attached to a wall (even though they can't be rewired until attached).
|
||||
- Fixed trying to bind multiple console commands to the same key with the "binkey" command crashing the game.
|
||||
- Fixed high-quality revolvers having no difference to normal-quality ones. They should get a 10% damage boost per quality level, but didn't due to incorrectly configured quality stats.
|
||||
- Fixed trying to bind multiple console commands to the same key with the "bindkey" command crashing the game.
|
||||
- Fixed high-quality revolvers having no difference to normal-quality ones. They should get a 10% damage boost per quality level but didn't, due to incorrectly configured quality stats.
|
||||
- Fixed multiple monster missions sometimes spawning the monsters close to each other, causing them to attack each other.
|
||||
- Fix monsters sometimes using wrong animation parameters while idling (or moving slowly).
|
||||
- Fixed monsters sometimes using the wrong animation parameters while idling (or moving slowly).
|
||||
- Fixed nuclear depth decoy using the same sprite as the normal depth decoy.
|
||||
- Fixed fractal guardian VitalityMultipliers being configured incorrectly (using the "type" attribute but with affliction identifiers instead of types).
|
||||
- Fixed incorrectly sized thalamus wall colliders, added background sprites to the walls.
|
||||
- Fixed "tried to overwrite a submarine that's not in a local package" error when loading and trying to save a submarine autosave file.
|
||||
- Fixed location portraits sometimes not showing up in the mission tab. Happened when we initialized the mission tab before the portrait had been loaded.
|
||||
- Fixed coilguns and chainguns not always playing the firing sound when fired. Happened because their audio clips were so long (albeit mostly silence) that firing the them continously lead to a ton of clips playing simultaneously, exhausting the available audio channels.
|
||||
- Fixed monster missions' sonar marker being placed incorrectly if a monster ends up inside the sub, making it look as if the monster was far outside the level. This often made it look like the monster was moving away from the sub when trying to "approach it".
|
||||
- Fixed security officer tutorial getting stuck if you equip the weapons and gear before the objective to do so appears.
|
||||
- Fixed bandolier (and other items that give bonuses when worn) giving the bonuses when the item is held.
|
||||
- Fixed Coilguns and Chainguns not always playing the firing sound when fired. Happened because their audio clips were so long (albeit mostly silent) that firing them continuously led to a ton of clips playing simultaneously, exhausting the available audio channels.
|
||||
- Fixed monster missions' sonar marker being placed incorrectly if a monster ends up inside the sub, making it look as if the monster was far outside the level. This often made it look like the monster was moving away from the sub when trying to approach its position as it appeared on the sonar?
|
||||
- Fixed bandolier (and other items that give bonuses when worn) giving bonuses when the item is held.
|
||||
- Fixed mod texts being briefly misaligned when scrolling down the list of unpublished mods.
|
||||
- Fixed light sprite rotation not getting refreshed when placing an attachable item on a wall when lighting has been disabled with console commands.
|
||||
- Fixed supercapacitors showing 1% as the initial recharge rate because the recharge rate defaulted to 10.
|
||||
- Fixed some ending options of the "good samaritan" outpost event not ending the event.
|
||||
- Fixed random (non-mission) events disappearing from outposts when you save and quit.
|
||||
|
||||
Submarine editor:
|
||||
- Fixed crashing when trying to multi-edit a string value in the sub editor.
|
||||
- Fixed dragged object becoming invisible if you bring the cursor over an UI element in the sub editor.
|
||||
- Fixed screwdrivers and wires in your "inventory" being included in the total item count in the sub editor's wiring mode.
|
||||
- Fixed entities that were below the cursor when starting to resize a structure staying highlighted during resizing.
|
||||
- Fixed sub editor treating the autosave interval as minutes instead of seconds (saving every 300 minutes instead of 300 seconds).
|
||||
|
||||
Modding:
|
||||
- The tutorials are now implemented using the scripted event system, and are fully moddable. New tutorials can be implemented in xml or using the event editor, and the system could potentially be used for other types of content too (scripted "scenarios" perhaps?).
|
||||
- Added DamageMultiplier and LaunchImpulse to Turret. LaunchImpulse is now defined on turrets instead of ammunition (total impulse is the sum of turret + ammunition).
|
||||
- Added SnapRopeOnNewAttack property to Attacks: allows characters to switch attacks without snapping ropes from previous attacks.
|
||||
- Added dividebylimbcount to Explosion, which determines whether the damage is spread out among limbs (if set to true).
|
||||
- UpgradeCategories with no upgrades in them are hidden from the upgrade menu (i.e. if you modify the upgrades so some of the vanilla categories no longer contain any upgrades, those categories won't be shown).
|
||||
- Added CheckTalentAction, which can be used in events to check whether a target has unlocked a specific talent.
|
||||
- Changed how submarine upgrades are calculated: now no longer adds previous levels' cost to the price, but rather relies on higher increasehigh values.
|
||||
- Made NPC personality traits a separate content type instead of defining them in the localization files.
|
||||
- Fixed OnDeath status effects defined in afflictions not working. Did not affect any vanilla content.
|
||||
- Fixed crash when controlling a character with more than 10 "Any" inventory slots. Did not affect any vanilla content.
|
||||
- Fixed custom husk appendages' textures failing to load.
|
||||
- Added new properties to StatusEffect's SpawnCharacter feature: Stun, AfflictionOnSpawn, AfflictionStrength, TransferControl, RemovePreviousCharacter, TransferBuffs, TransferAfflictions, TransferInventory.
|
||||
- Fixed bots always choosing their "personality trait" from the first 6 even if more are modded in.
|
||||
- Fixed affliction names and descriptions being empty if they're not available in the selected language or configured in the affliction .xml file directly.
|
||||
- Fixed custom husk afflictions not always working properly, because the vanilla husk affliction was sometimes used instead of the custom husk affliction.
|
||||
- Fixed ExtraLoad working the wrong way around on PowerTransfer components that generate/consume power (the extra load would supply power to the grid). Does not affect the vanilla game, because neither junction boxes or relays generate or consume power.
|
||||
- Fixed crashing if Afflictions defined in an Attack can't be found.
|
||||
- Fixed crashing if a Throwable has an OnActive StatusEffect that removes or kills the user.
|
||||
- Fixed items/structures falling back to the description defined in the .xml even if it's empty, if the description is not defined for the selected language. Now descriptions fall back to English when not defined for the selected language.
|
||||
- Fixed the "reloadwearables" and "loadwearable" console commands crashing the game when used outside the character editor.
|
||||
- Fixed character editor crash if you first reload textures and then recreate the ragdoll.
|
||||
- Fixed inability to localize item names if the name is defined directly in the item config.
|
||||
- Allowed defining where mineral mission resources are spawned using the "positiontype" attribute. The supported types are "MainPath", "SidePath", "Cave", and "AbyssCave".
|
||||
- Fixed console errors when trying to check int values with PropertyConditionals.
|
||||
- Fixed melee weapon's StrikingPowerMultiplier only affecting the afflictions defined in the Attack, not the ones defined in the status effect.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.18.15.2 (MacOS only)
|
||||
|
||||
Reference in New Issue
Block a user