v0.19.8.0

This commit is contained in:
Juan Pablo Arce
2022-09-28 21:30:52 -03:00
parent fec8131243
commit 3ca584f2fc
152 changed files with 1931 additions and 1071 deletions
@@ -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)
@@ -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;
@@ -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;
}
@@ -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;
}
@@ -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);
}
@@ -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;
}
}
@@ -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()
@@ -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
{
@@ -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; }
@@ -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)
{
@@ -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";
}
}
}
@@ -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;
}
@@ -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);
}
}
@@ -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,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;
}
@@ -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))
@@ -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)
@@ -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;
}
@@ -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
{