Release 1.10.5.0 - Autumn Update 2025
This commit is contained in:
@@ -21,6 +21,9 @@
|
||||
[Serialize(0, IsPropertySaveable.Yes, description: "The state to set the mission to, or how much to add to the state of the mission.")]
|
||||
public int State { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If set to true, the mission is forced to fail without a chance of retrying it.")]
|
||||
public bool ForceFailure { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public MissionStateAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
@@ -31,7 +34,7 @@
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (Operation == OperationType.Add && State == 0)
|
||||
if (Operation == OperationType.Add && State == 0 && !ForceFailure)
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in event \"{parentEvent.Prefab.Identifier}\": {nameof(MissionStateAction)} is set to add 0 to the mission state, which will do nothing.",
|
||||
contentPackage: element.ContentPackage);
|
||||
@@ -54,6 +57,11 @@
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
if (mission.Prefab.Identifier != MissionIdentifier) { continue; }
|
||||
if (ForceFailure)
|
||||
{
|
||||
mission.ForceFailure = true;
|
||||
}
|
||||
|
||||
switch (Operation)
|
||||
{
|
||||
case OperationType.Set:
|
||||
|
||||
@@ -410,16 +410,39 @@ namespace Barotrauma
|
||||
public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable<Identifier> moduleFlags = null, IEnumerable<Identifier> spawnpointTags = null, bool asFarAsPossibleFromAirlock = false, bool requireTaggedSpawnPoint = false, bool allowInPlayerView = true)
|
||||
{
|
||||
bool requireHull = spawnLocation == SpawnLocationType.MainSub || spawnLocation == SpawnLocationType.Outpost;
|
||||
List<WayPoint> potentialSpawnPoints = WayPoint.WayPointList.FindAll(wp => IsValidSubmarineType(spawnLocation, wp.Submarine) && (wp.CurrentHull != null || !requireHull));
|
||||
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && wp.IsTraversable);
|
||||
|
||||
IEnumerable<WayPoint> potentialSpawnPoints = WayPoint.WayPointList.FindAll(wp => IsValidSubmarineType(spawnLocation, wp.Submarine) && (wp.CurrentHull != null || !requireHull));
|
||||
potentialSpawnPoints = potentialSpawnPoints.Where(wp => wp.ConnectedDoor == null && wp.Ladders == null && wp.IsTraversable);
|
||||
|
||||
//find spawnpoints with the desired type, or any random spawnpoints if not specified
|
||||
IEnumerable<WayPoint> spawnPointsWithCorrectType;
|
||||
if (spawnPointType.HasValue)
|
||||
{
|
||||
spawnPointsWithCorrectType = potentialSpawnPoints.Where(wp =>
|
||||
spawnPointType.Value.HasFlag(wp.SpawnType) &&
|
||||
//need to handle zero (SpawnType.Path) separately, because spawnPointType will always have the flag 0
|
||||
(wp.SpawnType != 0 || spawnPointType.Value == 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPointsWithCorrectType = potentialSpawnPoints.Where(wp => wp.SpawnType != SpawnType.Path);
|
||||
}
|
||||
if (spawnPointsWithCorrectType.Any())
|
||||
{
|
||||
potentialSpawnPoints = spawnPointsWithCorrectType;
|
||||
}
|
||||
|
||||
//with correct module flags, if there are any
|
||||
if (moduleFlags != null && moduleFlags.Any())
|
||||
{
|
||||
var spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull is Hull h && h.OutpostModuleTags.Any(moduleFlags.Contains));
|
||||
if (spawnPoints.Any())
|
||||
var spawnPointsWithCorrectFlags = potentialSpawnPoints.Where(wp => wp.CurrentHull is Hull h && h.OutpostModuleTags.Any(moduleFlags.Contains));
|
||||
if (spawnPointsWithCorrectFlags.Any())
|
||||
{
|
||||
potentialSpawnPoints = spawnPoints.ToList();
|
||||
potentialSpawnPoints = spawnPointsWithCorrectFlags.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
//with correct spawn point tags, if there are any
|
||||
if (spawnpointTags != null && spawnpointTags.Any())
|
||||
{
|
||||
var spawnPointsWithTag = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable));
|
||||
@@ -461,16 +484,8 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<WayPoint> validSpawnPoints;
|
||||
if (spawnPointType.HasValue)
|
||||
{
|
||||
validSpawnPoints = potentialSpawnPoints.FindAll(wp => spawnPointType.Value.HasFlag(wp.SpawnType));
|
||||
}
|
||||
else
|
||||
{
|
||||
validSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.SpawnType != SpawnType.Path);
|
||||
if (!validSpawnPoints.Any()) { validSpawnPoints = potentialSpawnPoints; }
|
||||
}
|
||||
//spawnpoints that match the desired criteria found, choose the best one next
|
||||
IEnumerable<WayPoint> validSpawnPoints = potentialSpawnPoints;
|
||||
|
||||
//don't spawn in an airlock module if there are other options
|
||||
var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags.Contains("airlock".ToIdentifier()) ?? false);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -23,10 +24,12 @@ namespace Barotrauma
|
||||
private bool swarmSpawned;
|
||||
private readonly List<MonsterSet> monsterSets = new List<MonsterSet>();
|
||||
private readonly LocalizedString sonarLabel;
|
||||
private readonly ImmutableArray<Identifier> beaconTags;
|
||||
|
||||
public BeaconMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
|
||||
{
|
||||
swarmSpawned = false;
|
||||
beaconTags = prefab.ConfigElement.GetAttributeIdentifierArray("beacontags", []).ToImmutableArray();
|
||||
|
||||
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
|
||||
{
|
||||
@@ -185,6 +188,29 @@ namespace Barotrauma
|
||||
{
|
||||
levelData.HasBeaconStation = true;
|
||||
levelData.IsBeaconActive = false;
|
||||
|
||||
if (beaconTags.Length > 0)
|
||||
{
|
||||
var selectedBeacon = GetRandomBeaconByTags(beaconTags, levelData);
|
||||
if (selectedBeacon != null)
|
||||
{
|
||||
levelData.ForceBeaconStation = selectedBeacon;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Beacon mission \"{Prefab.Identifier}\" could not find a suitable beacon station with beacontags \"{string.Join(", ", beaconTags)}\" for level difficulty {levelData.Difficulty:F1}.",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SubmarineInfo GetRandomBeaconByTags(ImmutableArray<Identifier> tags, LevelData levelData)
|
||||
{
|
||||
return GetRandomSubmarineByTagsAndDifficulty(
|
||||
tags,
|
||||
levelData,
|
||||
s => s.IsBeacon,
|
||||
"beacon station");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace Barotrauma
|
||||
|
||||
protected static bool IsClient => GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
|
||||
private readonly CheckDataAction completeCheckDataAction;
|
||||
protected readonly CheckDataAction completeCheckDataAction;
|
||||
|
||||
public readonly ImmutableArray<LocalizedString> Headers;
|
||||
public readonly ImmutableArray<LocalizedString> Messages;
|
||||
@@ -110,9 +110,11 @@ namespace Barotrauma
|
||||
|
||||
public bool Failed
|
||||
{
|
||||
get { return failed; }
|
||||
get { return failed || ForceFailure; }
|
||||
}
|
||||
|
||||
public bool ForceFailure;
|
||||
|
||||
public virtual bool AllowRespawning
|
||||
{
|
||||
get { return true; }
|
||||
@@ -541,9 +543,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
completed =
|
||||
completed =
|
||||
!ForceFailure &&
|
||||
DetermineCompleted() &&
|
||||
(completeCheckDataAction == null ||completeCheckDataAction.GetSuccess());
|
||||
(completeCheckDataAction == null || completeCheckDataAction.GetSuccess());
|
||||
}
|
||||
if (completed)
|
||||
{
|
||||
@@ -569,6 +572,10 @@ namespace Barotrauma
|
||||
TimesAttempted++;
|
||||
|
||||
EndMissionSpecific(completed);
|
||||
if (ForceFailure)
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool DetermineCompleted();
|
||||
@@ -829,6 +836,51 @@ namespace Barotrauma
|
||||
cargoSpawnPos.Position.X + Rand.Range(-20.0f, 20.0f, Rand.RandSync.ServerAndClient),
|
||||
cargoRoom.Rect.Y - cargoRoom.Rect.Height + itemPrefab.Size.Y / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random submarine by tags, filtered by difficulty. Used by missions that force specific submarines (wrecks, beacons, etc.)
|
||||
/// </summary>
|
||||
/// <param name="tags">Mission tags to match against</param>
|
||||
/// <param name="seed">Random seed for selection</param>
|
||||
/// <param name="submarineSelector">Function to filter submarines by type (e.g., s => s.IsWreck)</param>
|
||||
/// <param name="submarineTypeName">Name of submarine type for error messages (e.g., "wreck", "beacon station")</param>
|
||||
/// <returns>Selected submarine, or null if none found</returns>
|
||||
protected static SubmarineInfo GetRandomSubmarineByTagsAndDifficulty(
|
||||
IEnumerable<Identifier> tags,
|
||||
LevelData levelData,
|
||||
Func<SubmarineInfo, bool> submarineSelector,
|
||||
string submarineTypeName)
|
||||
{
|
||||
var rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
|
||||
float levelDifficulty = levelData.Difficulty;
|
||||
|
||||
var submarinesWithTags = SubmarineInfo.SavedSubmarines
|
||||
.Where(submarineSelector)
|
||||
.Where(s =>
|
||||
{
|
||||
return s.GetExtraSubmarineInfo is { } extraInfo && (tags.None() || tags.Any(t => extraInfo.MissionTags.Contains(t)));
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var matchingSubmarines = submarinesWithTags
|
||||
.Where(s =>
|
||||
{
|
||||
return s.GetExtraSubmarineInfo is { } extraInfo &&
|
||||
levelDifficulty >= extraInfo.MinLevelDifficulty &&
|
||||
levelDifficulty <= extraInfo.MaxLevelDifficulty;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (matchingSubmarines.Count == 0)
|
||||
{
|
||||
if (submarinesWithTags.Count > 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Found {submarinesWithTags.Count} {submarineTypeName}(s) with matching tags \"{string.Join(", ", tags)}\", but none are suitable for level difficulty {levelDifficulty:F1}.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return matchingSubmarines[rand.Next(matchingSubmarines.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
class AbilityMissionMoneyGainMultiplier : AbilityObject, IAbilityValue, IAbilityMission
|
||||
|
||||
@@ -4,6 +4,7 @@ using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -240,6 +241,8 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private readonly float requiredDeliveryAmount;
|
||||
|
||||
private readonly ImmutableArray<Identifier> wreckTags;
|
||||
|
||||
private LocalizedString pickedUpMessage;
|
||||
|
||||
/// <summary>
|
||||
@@ -311,12 +314,13 @@ namespace Barotrauma
|
||||
: base(prefab, locations, sub)
|
||||
{
|
||||
requiredDeliveryAmount = prefab.ConfigElement.GetAttributeFloat(nameof(requiredDeliveryAmount), 0.98f);
|
||||
|
||||
//LevelData may not be instantiated at this point, in that case use the name identifier of the location
|
||||
rng = new MTRandom(ToolBox.StringToInt(
|
||||
locations[0].LevelData?.Seed ?? locations[0].NameIdentifier.Value +
|
||||
locations[1].LevelData?.Seed ?? locations[1].NameIdentifier.Value));
|
||||
|
||||
wreckTags = prefab.ConfigElement.GetAttributeIdentifierArray("wrecktags", []).ToImmutableArray();
|
||||
|
||||
partiallyRetrievedMessage = GetMessage(nameof(partiallyRetrievedMessage));
|
||||
allRetrievedMessage = GetMessage(nameof(allRetrievedMessage));
|
||||
pickedUpMessage = GetMessage(nameof(pickedUpMessage));
|
||||
@@ -756,5 +760,31 @@ namespace Barotrauma
|
||||
target.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public override void AdjustLevelData(LevelData levelData)
|
||||
{
|
||||
if (wreckTags.Length > 0)
|
||||
{
|
||||
var selectedWreck = GetRandomWreckByTags(wreckTags, levelData);
|
||||
if (selectedWreck != null)
|
||||
{
|
||||
levelData.ForceWreck = selectedWreck;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Salvage mission \"{Prefab.Identifier}\" could not find a suitable wreck with wrecktags \"{string.Join(", ", wreckTags)}\" for level difficulty {levelData.Difficulty:F1}.",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SubmarineInfo GetRandomWreckByTags(ImmutableArray<Identifier> tags, LevelData levelData)
|
||||
{
|
||||
return GetRandomSubmarineByTagsAndDifficulty(
|
||||
tags,
|
||||
levelData,
|
||||
s => s.IsWreck,
|
||||
"wreck");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user