v1.2.6.0 (Winter Update)

This commit is contained in:
Regalis11
2023-12-14 16:11:27 +02:00
parent af8cc89fce
commit b91e85559d
375 changed files with 7771 additions and 2874 deletions
@@ -1,3 +1,6 @@
using Barotrauma.Extensions;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
@@ -8,21 +11,56 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
private PropertyConditional Conditional { get; }
[Serialize(PropertyConditional.LogicalOperatorType.Or, IsPropertySaveable.Yes)]
public PropertyConditional.LogicalOperatorType LogicalOperator { get; set; }
private ImmutableArray<PropertyConditional> Conditionals { get; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier ApplyTagToLinkedHulls { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Tag to apply to the hull the target item is inside when the item is used.")]
public Identifier ApplyTagToHull { get; set; }
public CheckConditionalAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (TargetTag.IsEmpty)
{
DebugConsole.LogError($"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.",
contentPackage: parentEvent.Prefab.ContentPackage);
}
Conditional = PropertyConditional.FromXElement(element, IsNotTargetTagAttribute).FirstOrDefault();
if (Conditional == null)
var conditionalElements = element.GetChildElements("Conditional");
if (conditionalElements.None())
{
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
//backwards compatibility
Conditionals = PropertyConditional.FromXElement(element, IsConditionalAttribute).ToImmutableArray();
}
else
{
var conditionalList = new List<PropertyConditional>();
foreach (ContentXElement subElement in conditionalElements)
{
conditionalList.AddRange(PropertyConditional.FromXElement(subElement));
break;
}
Conditionals = conditionalList.ToImmutableArray();
}
static bool IsNotTargetTagAttribute(XAttribute attribute) => attribute.NameAsIdentifier() != "targettag";
if (Conditionals.None())
{
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.",
contentPackage: parentEvent.Prefab.ContentPackage);
}
static bool IsConditionalAttribute(XAttribute attribute)
{
var nameAsIdentifier = attribute.NameAsIdentifier();
return
nameAsIdentifier != nameof(TargetTag) &&
nameAsIdentifier != nameof(LogicalOperator) &&
nameAsIdentifier != nameof(ApplyTagToLinkedHulls) &&
nameAsIdentifier != nameof(ApplyTagToHull);
}
}
private string GetEventName()
@@ -32,31 +70,65 @@ namespace Barotrauma
protected override bool? DetermineSuccess()
{
ISerializableEntity target = null;
IEnumerable<ISerializableEntity> targets = null;
if (!TargetTag.IsEmpty)
{
foreach (var t in ParentEvent.GetTargets(TargetTag))
targets = ParentEvent.GetTargets(TargetTag).OfType<ISerializableEntity>();
}
if (targets.None())
{
DebugConsole.LogError($"{nameof(CheckConditionalAction)} error: {GetEventName()} uses a {nameof(CheckConditionalAction)} but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
if (targets.None() || Conditionals.None())
{
foreach (var target in targets)
{
if (t is ISerializableEntity e)
{
target = e;
break;
}
ApplyTagsToHulls(target as Entity, ApplyTagToHull, ApplyTagToLinkedHulls);
}
}
if (target == null)
{
DebugConsole.LogError($"{nameof(CheckConditionalAction)} error: {GetEventName()} uses a {nameof(CheckConditionalAction)} but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
}
if (target == null || Conditional == null)
{
return true;
}
else
{
bool success = false;
foreach (var target in targets)
{
if (ConditionalsMatch(target))
{
success = true;
ApplyTagsToHulls(target as Entity, ApplyTagToHull, ApplyTagToLinkedHulls);
}
}
return success;
}
}
private bool ConditionalsMatch(ISerializableEntity target)
{
if (LogicalOperator == PropertyConditional.LogicalOperatorType.And)
{
return Conditionals.All(c => ConditionalMatches(target, c));
}
else
{
return Conditionals.Any(c => ConditionalMatches(target, c));
}
}
private static bool ConditionalMatches(ISerializableEntity target, PropertyConditional conditional)
{
if (target is Item item)
{
return item.ConditionalMatches(Conditional);
if (!conditional.TargetItemComponent.IsNullOrEmpty() &&
item.Components.None(ic => ic.Name == conditional.TargetItemComponent))
{
return false;
}
return item.ConditionalMatches(conditional);
}
return Conditional.Matches(target);
return conditional.Matches(target);
}
}
}
@@ -30,7 +30,8 @@ namespace Barotrauma
Condition = element.GetAttributeString("value", string.Empty)!;
if (string.IsNullOrEmpty(Condition))
{
DebugConsole.ThrowError($"Error in scripted event \"{parentEvent.Prefab.Identifier}\". CheckDataAction with no condition set ({element}).");
DebugConsole.ThrowError($"Error in scripted event \"{parentEvent.Prefab.Identifier}\". CheckDataAction with no condition set ({element}).",
contentPackage: element?.ContentPackage);
}
}
}
@@ -42,7 +43,8 @@ namespace Barotrauma
Condition = element.GetAttributeString("value", string.Empty)!;
if (string.IsNullOrEmpty(Condition))
{
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).");
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).",
contentPackage: element?.ContentPackage);
}
}
}
@@ -59,7 +61,8 @@ namespace Barotrauma
(Operator, string value) = PropertyConditional.ExtractComparisonOperatorFromConditionString(Condition);
if (Operator == PropertyConditional.ComparisonOperatorType.None)
{
DebugConsole.ThrowError($"{Condition} is invalid, it should start with an operator followed by a boolean or a floating point value.");
DebugConsole.ThrowError($"{Condition} is invalid, it should start with an operator followed by a boolean or a floating point value.",
contentPackage: ParentEvent?.Prefab?.ContentPackage);
return false;
}
@@ -77,7 +77,8 @@ namespace Barotrauma
ItemIdentifiers.None() &&
TargetTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.");
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.",
contentPackage: element.ContentPackage);
}
checkPercentage = element.GetAttribute(nameof(RequiredConditionalMatchPercentage)) is not null;
if (checkPercentage && conditionals.None())
@@ -86,7 +87,8 @@ namespace Barotrauma
}
if (Amount != 1 && checkPercentage)
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Cannot define both '{Amount}' and '{RequiredConditionalMatchPercentage}' in {nameof(CheckItemAction)}.");
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Cannot define both '{Amount}' and '{RequiredConditionalMatchPercentage}' in {nameof(CheckItemAction)}.",
contentPackage: element.ContentPackage);
}
}
@@ -32,7 +32,8 @@ namespace Barotrauma
var targetCharacters = ParentEvent.GetTargets(TargetTag);
if (targetCharacters.None())
{
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target characters were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target characters were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.",
contentPackage: ParentEvent.Prefab.ContentPackage);
return false;
}
foreach (var t in targetCharacters)
@@ -1,7 +1,5 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -31,7 +29,8 @@ namespace Barotrauma
}
default:
{
DebugConsole.ThrowError("CheckReputationAction requires a \"TargetType\" but none were specified.");
DebugConsole.ThrowError("CheckReputationAction requires a \"TargetType\" but none were specified.",
contentPackage: ParentEvent.Prefab.ContentPackage);
break;
}
}
@@ -41,7 +40,8 @@ namespace Barotrauma
protected override bool GetBool(CampaignMode campaignMode)
{
DebugConsole.ThrowError("Boolean comparison cannot be applied to reputations.");
DebugConsole.ThrowError("Boolean comparison cannot be applied to reputations.",
contentPackage: ParentEvent.Prefab.ContentPackage);
return false;
}
@@ -87,13 +87,13 @@ namespace Barotrauma
#if DEBUG
void Error(string errorMsg)
{
DebugConsole.ThrowError(errorMsg);
DebugConsole.ThrowError(errorMsg, contentPackage: ParentEvent.Prefab.ContentPackage);
}
#else
void Error(string errorMsg)
{
DebugConsole.LogError(errorMsg);
DebugConsole.LogError(errorMsg, contentPackage: ParentEvent.Prefab.ContentPackage);
}
#endif
}
@@ -17,7 +17,8 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError($"Cannot use the action {nameof(CheckTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.");
DebugConsole.ThrowError($"Cannot use the action {nameof(CheckTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.",
contentPackage: element.ContentPackage);
}
}
@@ -16,7 +16,8 @@ namespace Barotrauma
{
if (parentEvent is not TraitorEvent)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\" - {nameof(CheckTraitorVoteAction)} can only be used in traitor events.");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\" - {nameof(CheckTraitorVoteAction)} can only be used in traitor events.",
contentPackage: element.ContentPackage);
}
}
@@ -1,5 +1,6 @@
#nullable enable
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma
{
@@ -8,12 +9,18 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes, description: "Tag of the entity to do the visibility check from.")]
public Identifier EntityTag { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Entities that also have this tag are excluded.")]
public Identifier ExcludedEntityTag { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Tag of the entity to do the visibility check to.")]
public Identifier TargetTag { get; set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Does the entity need to be facing the target? Only valid if the entity is a character.")]
public bool CheckFacing { get; set; }
[Serialize(1000.0f, IsPropertySaveable.Yes, description: "Maximum distance between the targets.")]
public float MaxDistance { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Tag to apply to the entity who saw the target when the check succeeds.")]
public Identifier ApplyTagToEntity { get; set; }
@@ -31,11 +38,17 @@ namespace Barotrauma
{
foreach (var entity in ParentEvent.GetTargets(EntityTag))
{
if (!ExcludedEntityTag.IsEmpty)
{
if (ParentEvent.GetTargets(ExcludedEntityTag).Contains(entity)) { continue; }
}
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
if (!AllowSameEntity && entity == target) { continue; }
if (Character.IsTargetVisible(target, entity, CheckFacing))
{
if (Vector2.DistanceSquared(target.WorldPosition, entity.WorldPosition) > MaxDistance * MaxDistance) { continue; }
if (Character.IsTargetVisible(target, entity, seeThroughWindows: true, CheckFacing))
{
if (!ApplyTagToEntity.IsEmpty)
{
ParentEvent.AddTarget(ApplyTagToEntity, entity);
@@ -116,7 +116,8 @@ namespace Barotrauma
{
DebugConsole.ThrowError(
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\"" +
$" - unrecognized child element \"Replace\".");
$" - unrecognized child element \"Replace\".",
contentPackage: element.ContentPackage);
}
}
@@ -61,14 +61,16 @@ namespace Barotrauma
}
if (MinAmount > MaxAmount && MaxAmount > -1)
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {MinAmount} is larger than {MaxAmount} in {nameof(CountTargetsAction)}.");
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {MinAmount} is larger than {MaxAmount} in {nameof(CountTargetsAction)}.",
contentPackage: element.ContentPackage);
}
}
else
{
if (MinPercentageRelativeToTarget < 0.0f && MaxPercentageRelativeToTarget < 0.0f)
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Comparing to another target, but neither {nameof(MinPercentageRelativeToTarget)} or {nameof(MaxPercentageRelativeToTarget)} is set.");
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Comparing to another target, but neither {nameof(MinPercentageRelativeToTarget)} or {nameof(MaxPercentageRelativeToTarget)} is set.",
contentPackage: element.ContentPackage);
}
}
}
@@ -36,7 +36,8 @@ namespace Barotrauma
{
if (e.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase))
{
DebugConsole.ThrowError($"Error in event prefab \"{scriptedEvent.Prefab.Identifier}\". Status effect configured as a sub action (text: \"{Text}\"). Please configure status effects as child elements of a StatusEffectAction.");
DebugConsole.ThrowError($"Error in event prefab \"{scriptedEvent.Prefab.Identifier}\". Status effect configured as a sub action (text: \"{Text}\"). Please configure status effects as child elements of a StatusEffectAction.",
contentPackage: elem.ContentPackage);
continue;
}
var action = Instantiate(scriptedEvent, e);
@@ -140,14 +141,19 @@ namespace Barotrauma
Identifier typeName = element.Name.ToString().ToIdentifier();
if (typeName == "TutorialSegmentAction")
{
typeName = "EventObjectiveAction".ToIdentifier();
typeName = nameof(EventObjectiveAction).ToIdentifier();
}
else if (typeName == "TutorialHighlightAction")
{
typeName = nameof(HighlightAction).ToIdentifier();
}
actionType = Type.GetType("Barotrauma." + typeName, throwOnError: true, ignoreCase: true);
if (actionType == null) { throw new NullReferenceException(); }
}
catch
{
DebugConsole.ThrowError($"Could not find an {nameof(EventAction)} class of the type \"{element.Name}\".");
DebugConsole.ThrowError($"Could not find an {nameof(EventAction)} class of the type \"{element.Name}\".",
contentPackage: element.ContentPackage);
return null;
}
@@ -162,11 +168,36 @@ namespace Barotrauma
}
catch (Exception ex)
{
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString(),
contentPackage: element.ContentPackage);
return null;
}
}
protected void ApplyTagsToHulls(Entity entity, Identifier hullTag, Identifier linkedHullTag)
{
var currentHull = entity switch
{
Item item => item.CurrentHull,
Character character => character.CurrentHull,
_ => null,
};
if (currentHull == null) { return; }
if (!hullTag.IsEmpty)
{
ParentEvent.AddTarget(hullTag, currentHull);
}
if (!linkedHullTag.IsEmpty)
{
ParentEvent.AddTarget(linkedHullTag, currentHull);
foreach (var linkedHull in currentHull.GetLinkedEntities<Hull>())
{
ParentEvent.AddTarget(linkedHullTag, linkedHull);
}
}
}
/// <summary>
/// Rich test to display in debugdraw
/// </summary>
@@ -24,7 +24,8 @@ namespace Barotrauma
{
if (Id == Identifier.Empty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no id.");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no id.",
contentPackage: element.ContentPackage);
}
//append the target tag so logs targeted to different players don't interfere with each other even if they use the same Id
Id = (Id.ToString() + TargetTag).ToIdentifier();
@@ -42,7 +43,8 @@ namespace Barotrauma
{
if (Text.IsNullOrEmpty())
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no text set ({element}).");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no text set ({element}).",
contentPackage: element.ContentPackage);
}
else
{
@@ -2,7 +2,7 @@ namespace Barotrauma
{
partial class EventObjectiveAction : EventAction
{
public enum SegmentActionType { Trigger, Add, Complete, CompleteAndRemove, Remove, Fail, FailAndRemove };
public enum SegmentActionType { Trigger, Add, AddIfNotFound, Complete, CompleteAndRemove, Remove, Fail, FailAndRemove };
[Serialize(SegmentActionType.Trigger, IsPropertySaveable.Yes)]
public SegmentActionType Type { get; set; }
@@ -49,13 +49,15 @@ namespace Barotrauma
{
DebugConsole.ThrowError(
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\""+
$" - {nameof(TextTag)} will do nothing unless the action triggers a message box or a video.");
$" - {nameof(TextTag)} will do nothing unless the action triggers a message box or a video.",
contentPackage: element.ContentPackage);
}
if (element.GetChildElement("Replace") != null)
{
DebugConsole.ThrowError(
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\"" +
$" - unrecognized child element \"Replace\".");
$" - unrecognized child element \"Replace\".",
contentPackage: element.ContentPackage);
}
}
@@ -14,7 +14,8 @@ namespace Barotrauma
{
if (TargetTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveExpAction)} without a target tag (the action needs to know whose skill to check).");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveExpAction)} without a target tag (the action needs to know whose skill to check).",
contentPackage: element.ContentPackage);
}
}
@@ -17,7 +17,8 @@ namespace Barotrauma
{
if (TargetTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveSkillExpAction)} without a target tag (the action needs to know whose skill to check).");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveSkillExpAction)} without a target tag (the action needs to know whose skill to check).",
contentPackage: element.ContentPackage);
}
}
@@ -5,17 +5,31 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
public string Name { get; set; }
[Serialize(-1, IsPropertySaveable.Yes)]
public int MaxTimes { get; set; }
private int counter;
public GoTo(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
public override bool IsFinished(ref string goTo)
{
goTo = Name;
if (counter < MaxTimes || MaxTimes <= 0)
{
goTo = Name;
counter++;
}
return true;
}
public override string ToDebugString()
{
return $"[-] Go to label \"{Name}\"";
string msg = $"[-] Go to label \"{Name}\"";
if (MaxTimes > 0)
{
msg += $" ({counter}/{MaxTimes})";
}
return msg;
}
public override void Reset() { }
@@ -0,0 +1,43 @@
#nullable enable
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma;
partial class HighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Only the player controlling this character will see the highlight. If empty, all players will see it.")]
public Identifier TargetCharacter { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool State { get; set; }
private bool isFinished;
public HighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targetCharacters = TargetCharacter.IsEmpty ? null : ParentEvent.GetTargets(TargetCharacter).OfType<Character>();
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
SetHighlightProjSpecific(target, targetCharacters);
}
isFinished = true;
}
partial void SetHighlightProjSpecific(Entity entity, IEnumerable<Character>? targetCharacters);
public override bool IsFinished(ref string goToLabel) => isFinished;
public override void Reset() => isFinished = false;
}
@@ -37,11 +37,13 @@ namespace Barotrauma
{
if (MissionIdentifier.IsEmpty && MissionTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured.");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured.",
contentPackage: element.ContentPackage);
}
if (!MissionIdentifier.IsEmpty && !MissionTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.",
contentPackage: element.ContentPackage);
}
LocationTypes = element.GetAttributeIdentifierArray("locationtype", Array.Empty<Identifier>()).ToImmutableArray();
random = new MTRandom(parentEvent.RandomSeed);
@@ -103,11 +105,13 @@ namespace Barotrauma
{
if (!MissionIdentifier.IsEmpty)
{
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier,
invokingContentPackage: ParentEvent.Prefab.ContentPackage);
}
else if (!MissionTag.IsEmpty)
{
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag, random);
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag, random,
invokingContentPackage: ParentEvent.Prefab.ContentPackage);
}
if (campaign is MultiPlayerCampaign mpCampaign)
{
@@ -119,11 +123,11 @@ namespace Barotrauma
campaign.Map.Discover(unlockLocation, checkTalents: false);
if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] == null)
{
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.Name}\".");
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.DisplayName}\".");
}
else
{
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the connection from \"{unlockedMission.Locations[0].Name}\" to \"{unlockedMission.Locations[1].Name}\".");
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the connection from \"{unlockedMission.Locations[0].DisplayName}\" to \"{unlockedMission.Locations[1].DisplayName}\".");
}
#if CLIENT
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", unlockedMission.Name),
@@ -139,7 +143,8 @@ namespace Barotrauma
}
else
{
DebugConsole.AddWarning($"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" (LocationType: {string.Join(", ", LocationTypes)}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})");
DebugConsole.AddWarning($"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" (LocationType: {string.Join(", ", LocationTypes)}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})",
ParentEvent.Prefab.ContentPackage);
}
}
isFinished = true;
@@ -24,7 +24,8 @@ namespace Barotrauma
State = element.GetAttributeInt("value", State);
if (MissionIdentifier.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.",
contentPackage: element.ContentPackage);
}
}
@@ -12,7 +12,7 @@ namespace Barotrauma
public Identifier Type { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public string Name { get; set; }
public Identifier Name { get; set; }
private bool isFinished;
@@ -43,7 +43,8 @@ namespace Barotrauma
var faction = campaign.Factions.Find(f => f.Prefab.Identifier == Faction);
if (faction == null)
{
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{Faction}\".");
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{Faction}\".",
contentPackage: ParentEvent?.Prefab?.ContentPackage);
}
else
{
@@ -55,7 +56,8 @@ namespace Barotrauma
var secondaryFaction = campaign.Factions.Find(f => f.Prefab.Identifier == SecondaryFaction);
if (secondaryFaction == null)
{
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{SecondaryFaction}\".");
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{SecondaryFaction}\".",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
else
{
@@ -67,16 +69,17 @@ namespace Barotrauma
var locationType = LocationType.Prefabs.Find(lt => lt.Identifier == Type);
if (locationType == null)
{
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a location type with the identifier \"{Type}\".");
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a location type with the identifier \"{Type}\".",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
else if (!location.LocationTypeChangesBlocked)
{
location.ChangeType(campaign, locationType);
}
}
if (!string.IsNullOrEmpty(Name))
if (!Name.IsEmpty)
{
location.ForceName(TextManager.Get(Name).Fallback(Name).Value);
location.ForceName(Name);
}
}
}
@@ -30,7 +30,8 @@ namespace Barotrauma
var enums = Enum.GetValues(typeof(CharacterTeamType)).Cast<CharacterTeamType>();
if (!enums.Contains(TeamID))
{
DebugConsole.ThrowError($"Error in {nameof(NPCChangeTeamAction)} in the event {ParentEvent.Prefab.Identifier}. \"{TeamID}\" is not a valid Team ID. Valid values are {string.Join(',', Enum.GetNames(typeof(CharacterTeamType)))}.");
DebugConsole.ThrowError($"Error in {nameof(NPCChangeTeamAction)} in the event {ParentEvent.Prefab.Identifier}. \"{TeamID}\" is not a valid Team ID. Valid values are {string.Join(',', Enum.GetNames(typeof(CharacterTeamType)))}.",
contentPackage: element.ContentPackage);
}
}
@@ -100,7 +101,7 @@ namespace Barotrauma
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human);
if (subWaypoint != null)
{
npc.GiveIdCardTags(subWaypoint, requireSpawnPointTagsNotGiven: false, createNetworkEvent: true);
npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true);
}
}
}
@@ -13,11 +13,13 @@ namespace Barotrauma
{
if (Chance >= 1.0f)
{
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 1.0 (100%) or more, the action will always succeed.");
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 1.0 (100%) or more, the action will always succeed.",
contentPackage: element.ContentPackage);
}
else if (Chance <= 0.0f)
{
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 0 or less, the action will never succeed.");
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 0 or less, the action will never succeed.",
contentPackage: element.ContentPackage);
}
}
@@ -54,7 +54,8 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError($"Faction with the identifier \"{Identifier}\" was not found.");
DebugConsole.ThrowError($"Faction with the identifier \"{Identifier}\" was not found.",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
break;
@@ -66,7 +67,8 @@ namespace Barotrauma
}
default:
{
DebugConsole.ThrowError("ReputationAction requires a \"TargetType\" but none were specified.");
DebugConsole.ThrowError("ReputationAction requires a \"TargetType\" but none were specified.",
contentPackage: ParentEvent.Prefab.ContentPackage);
break;
}
}
@@ -14,7 +14,8 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError($"Cannot use the action {nameof(SetTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.");
DebugConsole.ThrowError($"Cannot use the action {nameof(SetTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.",
contentPackage: element.ContentPackage);
}
}
@@ -23,7 +23,8 @@ namespace Barotrauma
{
if (TargetTag.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": SkillCheckAction without a target tag (the action needs to know whose skill to check).");
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": SkillCheckAction without a target tag (the action needs to know whose skill to check).",
contentPackage: element.ContentPackage);
}
}
@@ -105,7 +105,8 @@ namespace Barotrauma
{
DebugConsole.ThrowError(
$"Error in even \"{(parentEvent.Prefab?.Identifier.ToString() ?? "unknown")}\". " +
$"The attribute \"submarinetype\" is not valid in {nameof(SpawnAction)}. Did you mean {nameof(SpawnLocation)}?");
$"The attribute \"submarinetype\" is not valid in {nameof(SpawnAction)}. Did you mean {nameof(SpawnLocation)}?",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
}
@@ -233,7 +234,8 @@ namespace Barotrauma
{
if (MapEntityPrefab.FindByIdentifier(ItemIdentifier) is not ItemPrefab itemPrefab)
{
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)");
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
else
{
@@ -256,7 +258,8 @@ namespace Barotrauma
if (spawnInventory == null)
{
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.");
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
}
@@ -90,31 +90,46 @@ namespace Barotrauma
private void TagPlayers()
{
AddTargetPredicate(Tag, e => e is Character c && c.IsPlayer && (!c.IsIncapacitated || !IgnoreIncapacitatedCharacters));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Character,
e => e is Character c && c.IsPlayer && (!c.IsIncapacitated || !IgnoreIncapacitatedCharacters));
}
private void TagTraitors()
{
AddTargetPredicate(Tags.Traitor, e => e is Character c && (c.IsPlayer || c.IsBot) && c.IsTraitor && !c.IsIncapacitated);
AddTargetPredicate(
Tags.Traitor,
ScriptedEvent.TargetPredicate.EntityType.Character,
e => e is Character c && (c.IsPlayer || c.IsBot) && c.IsTraitor && !c.IsIncapacitated);
}
private void TagNonTraitors()
{
AddTargetPredicate(Tags.NonTraitor, e => e is Character c && (c.IsPlayer || c.IsBot) && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
AddTargetPredicate(
Tags.NonTraitor,
ScriptedEvent.TargetPredicate.EntityType.Character,
e => e is Character c && (c.IsPlayer || c.IsBot) && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
}
private void TagNonTraitorPlayers()
{
AddTargetPredicate(Tags.NonTraitorPlayer, e => e is Character c && c.IsPlayer && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
AddTargetPredicate(
Tags.NonTraitorPlayer,
ScriptedEvent.TargetPredicate.EntityType.Character,
e => e is Character c && c.IsPlayer && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
}
private void TagBots(bool playerCrewOnly)
{
AddTargetPredicate(Tag, e =>
e is Character c &&
c.IsBot &&
(!c.IsIncapacitated || !IgnoreIncapacitatedCharacters) &&
(!playerCrewOnly || c.TeamID == CharacterTeamType.Team1));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Character,
e =>
e is Character c &&
c.IsBot &&
(!c.IsIncapacitated || !IgnoreIncapacitatedCharacters) &&
(!playerCrewOnly || c.TeamID == CharacterTeamType.Team1));
}
private void TagCrew()
@@ -139,42 +154,67 @@ namespace Barotrauma
private void TagStructuresByIdentifier(Identifier identifier)
{
AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.Prefab.Identifier == identifier);
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Structure,
e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.Prefab.Identifier == identifier);
}
private void TagStructuresBySpecialTag(Identifier tag)
{
AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.SpecialTag.ToIdentifier() == tag);
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Structure,
e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.SpecialTag.ToIdentifier() == tag);
}
private void TagItemsByIdentifier(Identifier identifier)
{
AddTargetPredicate(Tag, e => e is Item it && IsValidItem(it) && it.Prefab.Identifier == identifier);
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Item,
e => e is Item it && IsValidItem(it) && it.Prefab.Identifier == identifier);
}
private void TagItemsByTag(Identifier tag)
{
AddTargetPredicate(Tag, e => e is Item it && IsValidItem(it) && it.HasTag(tag));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Item,
e => e is Item it && IsValidItem(it) && it.HasTag(tag));
}
private void TagHulls()
{
AddTargetPredicate(Tag, e => e is Hull h && SubmarineTypeMatches(h.Submarine));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Hull,
e => e is Hull h && SubmarineTypeMatches(h.Submarine));
}
private void TagHullsByName(Identifier name)
{
AddTargetPredicate(Tag, e => e is Hull h && SubmarineTypeMatches(h.Submarine) && h.RoomName.Contains(name.Value, StringComparison.OrdinalIgnoreCase));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Hull,
e => e is Hull h && SubmarineTypeMatches(h.Submarine) && h.RoomName.Contains(name.Value, StringComparison.OrdinalIgnoreCase));
}
private void TagSubmarinesByType(Identifier type)
{
AddTargetPredicate(Tag, e => e is Submarine s && SubmarineTypeMatches(s) && (type.IsEmpty || type == s.Info?.Type.ToIdentifier()));
AddTargetPredicate(
Tag,
ScriptedEvent.TargetPredicate.EntityType.Submarine,
e => e is Submarine s && SubmarineTypeMatches(s) && (type.IsEmpty || type == s.Info?.Type.ToIdentifier()));
}
private bool IsValidItem(Item it)
{
return (!it.HiddenInGame || AllowHiddenItems) && SubmarineTypeMatches(it.Submarine);
return
(!it.HiddenInGame || AllowHiddenItems) &&
//if the item has just spawned, it may be in a hull but not moved into the coordinate space of the hull yet
//= it.Submarine still null
SubmarineTypeMatches(it.Submarine ?? it.CurrentHull?.Submarine ?? it.ParentInventory?.Owner?.Submarine);
}
private bool SubmarineTypeMatches(Submarine sub)
@@ -197,7 +237,7 @@ namespace Barotrauma
}
}
private void AddTargetPredicate(Identifier tag, Predicate<Entity> predicate)
private void AddTargetPredicate(Identifier tag, ScriptedEvent.TargetPredicate.EntityType entityType, Predicate<Entity> predicate)
{
if (ChoosePercentage > 0.0f)
{
@@ -209,7 +249,7 @@ namespace Barotrauma
}
else
{
ParentEvent.AddTargetPredicate(tag, predicate);
ParentEvent.AddTargetPredicate(tag, entityType, predicate);
mustRecheckTargets = true;
}
}
@@ -291,7 +331,8 @@ namespace Barotrauma
else
{
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
DebugConsole.ThrowError(errorMessage);
DebugConsole.ThrowError(errorMessage,
contentPackage: ParentEvent.Prefab?.ContentPackage);
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
}
}
@@ -36,7 +36,8 @@
var eventPrefab = EventSet.GetEventPrefab(Identifier);
if (eventPrefab == null)
{
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.");
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.",
contentPackage: ParentEvent.Prefab.ContentPackage);
}
else
{
@@ -1,33 +0,0 @@
namespace Barotrauma;
partial class TutorialHighlightAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool State { get; set; }
private bool isFinished;
public TutorialHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (GameMain.NetworkMember != null)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(TutorialHighlightAction)} is not supported in multiplayer.");
}
}
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;
}
@@ -28,7 +28,8 @@ namespace Barotrauma
{
if (ItemTag.IsEmpty && ItemIdentifier.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(WaitForItemFabricatedAction)} does't define either a tag or an identifier of the item to check.");
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(WaitForItemFabricatedAction)} does't define either a tag or an identifier of the item to check.",
contentPackage: element.ContentPackage);
}
foreach (var item in Item.ItemList)
{
@@ -30,11 +30,16 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes, description: "Tag to apply to the hull the target item is inside, and all the hulls it's linked to, when the item is used.")]
public Identifier ApplyTagToLinkedHulls { get; set; }
[Serialize(1, IsPropertySaveable.Yes)]
public int RequiredUseCount { get; set; }
private bool isFinished;
private readonly HashSet<Entity> targets = new HashSet<Entity>();
private readonly HashSet<ItemComponent> targetComponents = new HashSet<ItemComponent>();
private int useCount = 0;
private Identifier onUseEventIdentifier;
private Identifier OnUseEventIdentifier
{
@@ -58,6 +63,14 @@ namespace Barotrauma
private void OnItemUsed(Item item, Character user)
{
if (!UserTag.IsEmpty)
{
if (!ParentEvent.GetTargets(UserTag).Contains(user)) { return; }
}
useCount++;
if (useCount < RequiredUseCount) { return; }
if (!ApplyTagToItem.IsEmpty)
{
ParentEvent.AddTarget(ApplyTagToItem, item);
@@ -66,22 +79,7 @@ namespace Barotrauma
{
ParentEvent.AddTarget(ApplyTagToUser, user);
}
if (item.CurrentHull != null)
{
if (!ApplyTagToHull.IsEmpty)
{
ParentEvent.AddTarget(ApplyTagToHull, item.CurrentHull);
}
if (!ApplyTagToLinkedHulls.IsEmpty)
{
ParentEvent.AddTarget(ApplyTagToLinkedHulls, item.CurrentHull);
foreach (var linkedHull in item.CurrentHull.GetLinkedEntities<Hull>())
{
ParentEvent.AddTarget(ApplyTagToLinkedHulls, linkedHull);
}
}
}
ApplyTagsToHulls(item, ApplyTagToHull, ApplyTagToLinkedHulls);
DeregisterTargets();
isFinished = true;
}
@@ -142,6 +140,7 @@ namespace Barotrauma
public override void Reset()
{
isFinished = false;
useCount = 0;
DeregisterTargets();
}