Unstable 0.1500.5.0 (almost forgor edition 💀)
This commit is contained in:
@@ -226,11 +226,11 @@ namespace Barotrauma
|
||||
List<Item> potentialItems = SpawnLocation switch
|
||||
{
|
||||
SpawnLocationType.MainSub => Item.ItemList.FindAll(it => it.Submarine == Submarine.MainSub),
|
||||
SpawnLocationType.MainPath => Item.ItemList.FindAll(it => it.Submarine == null && it.ParentRuin == null),
|
||||
SpawnLocationType.Outpost => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsOutpost),
|
||||
SpawnLocationType.Wreck => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsWreck),
|
||||
SpawnLocationType.Ruin => Item.ItemList.FindAll(it => it.ParentRuin != null),
|
||||
SpawnLocationType.BeaconStation => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsBeacon),
|
||||
SpawnLocationType.MainPath => Item.ItemList.FindAll(it => it.Submarine == null),
|
||||
SpawnLocationType.Outpost => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsOutpost),
|
||||
SpawnLocationType.Wreck => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsWreck),
|
||||
SpawnLocationType.Ruin => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsRuin),
|
||||
SpawnLocationType.BeaconStation => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsBeacon),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
@@ -252,11 +252,11 @@ namespace Barotrauma
|
||||
List<WayPoint> potentialSpawnPoints = spawnLocation switch
|
||||
{
|
||||
SpawnLocationType.MainSub => WayPoint.WayPointList.FindAll(wp => wp.Submarine == Submarine.MainSub && wp.CurrentHull != null),
|
||||
SpawnLocationType.MainPath => WayPoint.WayPointList.FindAll(wp => wp.Submarine == null && wp.ParentRuin == null),
|
||||
SpawnLocationType.Outpost => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.CurrentHull != null && wp.Submarine.Info.IsOutpost),
|
||||
SpawnLocationType.Wreck => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsWreck),
|
||||
SpawnLocationType.Ruin => WayPoint.WayPointList.FindAll(wp => wp.ParentRuin != null),
|
||||
SpawnLocationType.BeaconStation => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsBeacon),
|
||||
SpawnLocationType.MainPath => WayPoint.WayPointList.FindAll(wp => wp.Submarine == null),
|
||||
SpawnLocationType.Outpost => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.CurrentHull != null && wp.Submarine.Info.IsOutpost),
|
||||
SpawnLocationType.Wreck => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsWreck),
|
||||
SpawnLocationType.Ruin => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsRuin),
|
||||
SpawnLocationType.BeaconStation => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsBeacon),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
|
||||
@@ -981,6 +981,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
case SubmarineType.Wreck:
|
||||
case SubmarineType.BeaconStation:
|
||||
case SubmarineType.Ruin:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.RuinGeneration;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AlienRuinMission : Mission
|
||||
{
|
||||
private readonly string[] targetItemIdentifiers;
|
||||
private readonly string[] targetEnemyIdentifiers;
|
||||
private readonly int minEnemyCount;
|
||||
private readonly HashSet<Entity> existingTargets = new HashSet<Entity>();
|
||||
private readonly HashSet<Character> spawnedTargets = new HashSet<Character>();
|
||||
private readonly HashSet<Entity> allTargets = new HashSet<Entity>();
|
||||
|
||||
private Ruin TargetRuin { get; set; }
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State == 0)
|
||||
{
|
||||
return allTargets.Where(t => (t is Item i && !IsItemDestroyed(i)) || (t is Character c && !IsEnemyDefeated(c))).Select(t => t.WorldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<Vector2>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AlienRuinMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
|
||||
{
|
||||
targetItemIdentifiers = prefab.ConfigElement.GetAttributeStringArray("targetitems", new string[0], convertToLowerInvariant: true);
|
||||
targetEnemyIdentifiers = prefab.ConfigElement.GetAttributeStringArray("targetenemies", new string[0], convertToLowerInvariant: true);
|
||||
minEnemyCount = prefab.ConfigElement.GetAttributeInt("minenemycount", 0);
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
existingTargets.Clear();
|
||||
spawnedTargets.Clear();
|
||||
allTargets.Clear();
|
||||
if (IsClient) { return; }
|
||||
TargetRuin = Level.Loaded?.Ruins?.GetRandom(randSync: Rand.RandSync.Server);
|
||||
if (TargetRuin == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): level contains no alien ruins");
|
||||
return;
|
||||
}
|
||||
if (targetItemIdentifiers.Length < 1 && targetEnemyIdentifiers.Length < 1)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): no target identifiers set in the mission definition");
|
||||
return;
|
||||
}
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (!targetItemIdentifiers.Contains(item.Prefab.Identifier)) { continue; }
|
||||
if (item.Submarine != TargetRuin.Submarine) { continue; }
|
||||
existingTargets.Add(item);
|
||||
allTargets.Add(item);
|
||||
}
|
||||
int existingEnemyCount = 0;
|
||||
foreach (var character in Character.CharacterList)
|
||||
{
|
||||
if (string.IsNullOrEmpty(character.SpeciesName)) { continue; }
|
||||
if (!targetEnemyIdentifiers.Contains(character.SpeciesName.ToLowerInvariant())) { continue; }
|
||||
if (character.Submarine != TargetRuin.Submarine) { continue; }
|
||||
existingTargets.Add(character);
|
||||
allTargets.Add(character);
|
||||
existingEnemyCount++;
|
||||
}
|
||||
if (existingEnemyCount < minEnemyCount)
|
||||
{
|
||||
var enemyPrefabs = new HashSet<CharacterPrefab>();
|
||||
foreach (string identifier in targetEnemyIdentifiers)
|
||||
{
|
||||
var prefab = CharacterPrefab.FindBySpeciesName(identifier);
|
||||
if (prefab != null)
|
||||
{
|
||||
enemyPrefabs.Add(prefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): could not find a character prefab with the species \"{identifier}\"");
|
||||
}
|
||||
}
|
||||
if (enemyPrefabs.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no enemy species defined that could be used to spawn more ({minEnemyCount - existingEnemyCount}) enemies");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < (minEnemyCount - existingEnemyCount); i++)
|
||||
{
|
||||
var prefab = enemyPrefabs.GetRandom();
|
||||
var spawnPos = TargetRuin.Submarine.GetWaypoints(false).GetRandom(w => w.CurrentHull != null)?.WorldPosition;
|
||||
if (!spawnPos.HasValue)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no valid spawn positions could be found for the additional ({minEnemyCount - existingEnemyCount}) enemies to be spawned");
|
||||
return;
|
||||
}
|
||||
var newEnemy = Character.Create(prefab.Identifier, spawnPos.Value, ToolBox.RandomSeed(8), createNetworkEvent: false);
|
||||
spawnedTargets.Add(newEnemy);
|
||||
allTargets.Add(newEnemy);
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("********** CLEAR RUIN MISSION INFO **********");
|
||||
DebugConsole.NewMessage($"Existing item targets: {existingTargets.Count - existingEnemyCount}");
|
||||
DebugConsole.NewMessage($"Existing enemy targets: {existingEnemyCount}");
|
||||
DebugConsole.NewMessage($"Spawned enemy targets: {spawnedTargets.Count}");
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void UpdateMissionSpecific(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
switch (State)
|
||||
{
|
||||
case 0:
|
||||
if (!AllTargetsEliminated()) { return; }
|
||||
State = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllTargetsEliminated()
|
||||
{
|
||||
foreach (var target in allTargets)
|
||||
{
|
||||
if (target is Item targetItem)
|
||||
{
|
||||
if (!IsItemDestroyed(targetItem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (target is Character targetEnemy)
|
||||
{
|
||||
if (!IsEnemyDefeated(targetEnemy))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in Alien Ruin mission (\"{Prefab.Identifier}\"): unexpected target of type {target?.GetType()?.ToString()}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
|
||||
|
||||
private bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (AllTargetsEliminated())
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,55 +198,14 @@ namespace Barotrauma
|
||||
if (requiredDeliveryAmount <= 0.0f) { requiredDeliveryAmount = 1.0f; }
|
||||
}
|
||||
|
||||
private ItemPrefab FindItemPrefab(XElement element)
|
||||
{
|
||||
ItemPrefab itemPrefab;
|
||||
if (element.Attribute("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in cargo mission \"" + Name + "\" - use item identifiers instead of names to configure the items.");
|
||||
string itemName = element.GetAttributeString("name", "");
|
||||
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn item for cargo mission: item prefab \"" + itemName + "\" not found");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string itemIdentifier = element.GetAttributeString("identifier", "");
|
||||
itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn item for cargo mission: item prefab \"" + itemIdentifier + "\" not found");
|
||||
}
|
||||
}
|
||||
return itemPrefab;
|
||||
}
|
||||
|
||||
|
||||
private void LoadItemAsChild(XElement element, Item parent)
|
||||
{
|
||||
ItemPrefab itemPrefab = FindItemPrefab(element);
|
||||
|
||||
WayPoint cargoSpawnPos = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub, useSyncedRand: true);
|
||||
if (cargoSpawnPos == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn items for cargo mission, cargo spawnpoint not found");
|
||||
return;
|
||||
}
|
||||
Vector2? position = GetCargoSpawnPosition(itemPrefab, out Submarine cargoRoomSub);
|
||||
if (!position.HasValue) { return; }
|
||||
|
||||
var cargoRoom = cargoSpawnPos.CurrentHull;
|
||||
if (cargoRoom == null)
|
||||
{
|
||||
DebugConsole.ThrowError("A waypoint marked as Cargo must be placed inside a room!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 position = new Vector2(
|
||||
cargoSpawnPos.Position.X + Rand.Range(-20.0f, 20.0f, Rand.RandSync.Server),
|
||||
cargoRoom.Rect.Y - cargoRoom.Rect.Height + itemPrefab.Size.Y / 2);
|
||||
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine)
|
||||
var item = new Item(itemPrefab, position.Value, cargoRoomSub)
|
||||
{
|
||||
SpawnedInOutpost = true,
|
||||
AllowStealing = false
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
state = value;
|
||||
TryTriggerEvents(state);
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(this, state);
|
||||
GameMain.Server?.UpdateMissionState(this);
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
}
|
||||
@@ -347,7 +347,10 @@ namespace Barotrauma
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode campaign)) { return; }
|
||||
int reward = GetReward(Submarine.MainSub);
|
||||
|
||||
float baseExperienceGain = reward * 0.15f;
|
||||
float baseExperienceGain = reward * 0.1f;
|
||||
|
||||
float difficultyMultiplier = 1 + level.Difficulty / 100f;
|
||||
baseExperienceGain *= difficultyMultiplier;
|
||||
|
||||
IEnumerable<Character> crewCharacters = GameSession.GetSessionCrewCharacters();
|
||||
|
||||
@@ -471,5 +474,55 @@ namespace Barotrauma
|
||||
|
||||
return spawnedCharacter;
|
||||
}
|
||||
|
||||
protected ItemPrefab FindItemPrefab(XElement element)
|
||||
{
|
||||
ItemPrefab itemPrefab;
|
||||
if (element.Attribute("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission \"{Name}\" - use item identifiers instead of names to configure the items");
|
||||
string itemName = element.GetAttributeString("name", "");
|
||||
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't spawn item for mission \"{Name}\": item prefab \"{itemName}\" not found");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string itemIdentifier = element.GetAttributeString("identifier", "");
|
||||
itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't spawn item for mission \"{Name}\": item prefab \"{itemIdentifier}\" not found");
|
||||
}
|
||||
}
|
||||
return itemPrefab;
|
||||
}
|
||||
|
||||
protected Vector2? GetCargoSpawnPosition(ItemPrefab itemPrefab, out Submarine cargoRoomSub)
|
||||
{
|
||||
cargoRoomSub = null;
|
||||
|
||||
WayPoint cargoSpawnPos = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub, useSyncedRand: true);
|
||||
if (cargoSpawnPos == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't spawn items for mission \"{Name}\": no waypoints marked as Cargo were found");
|
||||
return null;
|
||||
}
|
||||
|
||||
var cargoRoom = cargoSpawnPos.CurrentHull;
|
||||
if (cargoRoom == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't spawn items for mission \"{Name}\": waypoints marked as Cargo must be placed inside a room");
|
||||
return null;
|
||||
}
|
||||
|
||||
cargoRoomSub = cargoRoom.Submarine;
|
||||
|
||||
return new Vector2(
|
||||
cargoSpawnPos.Position.X + Rand.Range(-20.0f, 20.0f, Rand.RandSync.Server),
|
||||
cargoRoom.Rect.Y - cargoRoom.Rect.Height + itemPrefab.Size.Y / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ namespace Barotrauma
|
||||
Escort = 0x100,
|
||||
Pirate = 0x200,
|
||||
GoTo = 0x400,
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat | AbandonedOutpost | Escort | Pirate | GoTo
|
||||
ScanAlienRuins = 0x800,
|
||||
ClearAlienRuins = 0x1000,
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat | AbandonedOutpost | Escort | Pirate | GoTo | ScanAlienRuins | ClearAlienRuins
|
||||
}
|
||||
|
||||
partial class MissionPrefab
|
||||
@@ -40,7 +42,9 @@ namespace Barotrauma
|
||||
{ MissionType.AbandonedOutpost, typeof(AbandonedOutpostMission) },
|
||||
{ MissionType.Escort, typeof(EscortMission) },
|
||||
{ MissionType.Pirate, typeof(PirateMission) },
|
||||
{ MissionType.GoTo, typeof(GoToMission) }
|
||||
{ MissionType.GoTo, typeof(GoToMission) },
|
||||
{ MissionType.ScanAlienRuins, typeof(ScanMission) },
|
||||
{ MissionType.ClearAlienRuins, typeof(AlienRuinMission) }
|
||||
};
|
||||
public static readonly Dictionary<MissionType, Type> PvPMissionClasses = new Dictionary<MissionType, Type>()
|
||||
{
|
||||
@@ -372,6 +376,11 @@ namespace Barotrauma
|
||||
var connection = from.Connections.Find(c => c.Locations.Contains(from) && c.Locations.Contains(to));
|
||||
if (connection?.LevelData == null || !connection.LevelData.HasBeaconStation || connection.LevelData.IsBeaconActive) { return false; }
|
||||
}
|
||||
else if (Type == MissionType.ScanAlienRuins || Type == MissionType.ClearAlienRuins)
|
||||
{
|
||||
var connection = from.Connections.Find(c => c.Locations.Contains(from) && c.Locations.Contains(to));
|
||||
if (connection?.LevelData == null || connection.LevelData.GenerationParams.RuinCount < 1) { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,12 +126,12 @@ namespace Barotrauma
|
||||
item = suitableItems.FirstOrDefault(it => Vector2.DistanceSquared(it.WorldPosition, position) < 1000.0f);
|
||||
break;
|
||||
case Level.PositionType.Ruin:
|
||||
item = suitableItems.FirstOrDefault(it => it.ParentRuin != null && it.ParentRuin.Area.Contains(position));
|
||||
break;
|
||||
case Level.PositionType.Wreck:
|
||||
foreach (Item it in suitableItems)
|
||||
{
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
if (it.Submarine?.Info == null) { continue; }
|
||||
if (spawnPositionType == Level.PositionType.Ruin && it.Submarine.Info.Type != SubmarineType.Ruin) { continue; }
|
||||
if (spawnPositionType == Level.PositionType.Wreck && it.Submarine.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
Rectangle worldBorders = it.Submarine.Borders;
|
||||
worldBorders.Location += it.Submarine.WorldPosition.ToPoint();
|
||||
if (Submarine.RectContains(worldBorders, it.WorldPosition))
|
||||
@@ -178,10 +178,10 @@ namespace Barotrauma
|
||||
{
|
||||
case Level.PositionType.Cave:
|
||||
case Level.PositionType.MainPath:
|
||||
if (it.Submarine != null || it.ParentRuin != null) { continue; }
|
||||
if (it.Submarine != null) { continue; }
|
||||
break;
|
||||
case Level.PositionType.Ruin:
|
||||
if (it.ParentRuin == null) { continue; }
|
||||
if (it.Submarine?.Info == null || !it.Submarine.Info.IsRuin) { continue; }
|
||||
break;
|
||||
case Level.PositionType.Wreck:
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.RuinGeneration;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class ScanMission : Mission
|
||||
{
|
||||
private readonly XElement itemConfig;
|
||||
private readonly List<Item> startingItems = new List<Item>();
|
||||
private readonly List<Scanner> scanners = new List<Scanner>();
|
||||
private readonly Dictionary<Item, ushort> parentInventoryIDs = new Dictionary<Item, ushort>();
|
||||
private readonly Dictionary<Item, byte> parentItemContainerIndices = new Dictionary<Item, byte>();
|
||||
private readonly int targetsToScan;
|
||||
private readonly Dictionary<WayPoint, bool> scanTargets = new Dictionary<WayPoint, bool>();
|
||||
private readonly HashSet<WayPoint> newTargetsScanned = new HashSet<WayPoint>();
|
||||
private readonly float minTargetDistance, minTargetDistanceSquared;
|
||||
|
||||
|
||||
private Ruin TargetRuin { get; set; }
|
||||
|
||||
private bool AllTargetsScanned
|
||||
{
|
||||
get
|
||||
{
|
||||
return scanTargets.Any() && scanTargets.All(kvp => kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State > 0)
|
||||
{
|
||||
return Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else if (scanTargets.Any())
|
||||
{
|
||||
return scanTargets
|
||||
.Where(kvp => !kvp.Value)
|
||||
.Select(kvp => kvp.Key.WorldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<Vector2>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public ScanMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
|
||||
{
|
||||
itemConfig = prefab.ConfigElement.Element("Items");
|
||||
targetsToScan = prefab.ConfigElement.GetAttributeInt("targets", 1);
|
||||
minTargetDistance = prefab.ConfigElement.GetAttributeFloat("mintargetdistance", 0.0f);
|
||||
minTargetDistanceSquared = minTargetDistance * minTargetDistance;
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (IsClient) { return; }
|
||||
|
||||
if (itemConfig == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to initialize a Scan mission: item config is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var element in itemConfig.Elements())
|
||||
{
|
||||
LoadItem(element, null);
|
||||
}
|
||||
GetScanners();
|
||||
|
||||
TargetRuin = Level.Loaded?.Ruins?.GetRandom(randSync: Rand.RandSync.Server);
|
||||
if (TargetRuin == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to initialize a Scan mission: level contains no alien ruins");
|
||||
return;
|
||||
}
|
||||
|
||||
var availableWaypoints = TargetRuin.Submarine.GetWaypoints(false);
|
||||
availableWaypoints.RemoveAll(wp => wp.CurrentHull == null);
|
||||
if (availableWaypoints.Count < targetsToScan)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({availableWaypoints.Count} < {targetsToScan})");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < targetsToScan; i++)
|
||||
{
|
||||
var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.Server);
|
||||
scanTargets.Add(selectedWaypoint, false);
|
||||
availableWaypoints.Remove(selectedWaypoint);
|
||||
if (i < (targetsToScan - 1))
|
||||
{
|
||||
availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull);
|
||||
availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared);
|
||||
if (availableWaypoints.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
startingItems.Clear();
|
||||
parentInventoryIDs.Clear();
|
||||
parentItemContainerIndices.Clear();
|
||||
scanners.Clear();
|
||||
TargetRuin = null;
|
||||
scanTargets.Clear();
|
||||
}
|
||||
|
||||
private void LoadItem(XElement element, Item parent)
|
||||
{
|
||||
var itemPrefab = FindItemPrefab(element);
|
||||
Vector2? position = GetCargoSpawnPosition(itemPrefab, out Submarine cargoRoomSub);
|
||||
if (!position.HasValue) { return; }
|
||||
var item = new Item(itemPrefab, position.Value, cargoRoomSub);
|
||||
item.FindHull();
|
||||
startingItems.Add(item);
|
||||
if (parent?.GetComponent<ItemContainer>() is ItemContainer itemContainer)
|
||||
{
|
||||
parentInventoryIDs.Add(item, parent.ID);
|
||||
parentItemContainerIndices.Add(item, (byte)parent.GetComponentIndex(itemContainer));
|
||||
parent.Combine(item, user: null);
|
||||
}
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
int amount = subElement.GetAttributeInt("amount", 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
LoadItem(subElement, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetScanners()
|
||||
{
|
||||
foreach (var startingItem in startingItems)
|
||||
{
|
||||
if (startingItem.GetComponent<Scanner>() is Scanner scanner)
|
||||
{
|
||||
scanner.OnScanStarted += OnScanStarted;
|
||||
if (!IsClient)
|
||||
{
|
||||
scanner.OnScanCompleted += OnScanCompleted;
|
||||
}
|
||||
scanners.Add(scanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScanStarted(Scanner scanner)
|
||||
{
|
||||
float scanRadiusSquared = scanner.ScanRadius * scanner.ScanRadius;
|
||||
foreach (var kvp in scanTargets)
|
||||
{
|
||||
if (!IsValidScanPosition(scanner, kvp, scanRadiusSquared)) { continue; }
|
||||
scanner.DisplayProgressBar = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScanCompleted(Scanner scanner)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
newTargetsScanned.Clear();
|
||||
float scanRadiusSquared = scanner.ScanRadius * scanner.ScanRadius;
|
||||
foreach (var kvp in scanTargets)
|
||||
{
|
||||
if (!IsValidScanPosition(scanner, kvp, scanRadiusSquared)) { continue; }
|
||||
newTargetsScanned.Add(kvp.Key);
|
||||
}
|
||||
foreach (var wp in newTargetsScanned)
|
||||
{
|
||||
scanTargets[wp] = true;
|
||||
}
|
||||
#if SERVER
|
||||
// Server should make sure that the clients' scan target status is in-sync
|
||||
GameMain.Server?.UpdateMissionState(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool IsValidScanPosition(Scanner scanner, KeyValuePair<WayPoint, bool> scanStatus, float scanRadiusSquared)
|
||||
{
|
||||
if (scanStatus.Value) { return false; }
|
||||
if (scanStatus.Key.Submarine != scanner.Item.Submarine) { return false; }
|
||||
if (Vector2.DistanceSquared(scanStatus.Key.WorldPosition, scanner.Item.WorldPosition) > scanRadiusSquared) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void UpdateMissionSpecific(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
switch (State)
|
||||
{
|
||||
case 0:
|
||||
if (!AllTargetsScanned) { return; }
|
||||
State = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (AllTargetsScanned && 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;
|
||||
|
||||
bool AllScannersReturned()
|
||||
{
|
||||
foreach (var scanner in scanners)
|
||||
{
|
||||
if (scanner?.Item == null || scanner.Item.Removed) { return false; }
|
||||
var owner = scanner.Item.GetRootInventoryOwner();
|
||||
if (owner.Submarine != null && owner.Submarine.Info.Type == SubmarineType.Player)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (owner is Character c && c.Info != null && GameMain.GameSession.CrewManager.CharacterInfos.Contains(c.Info))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,11 +285,10 @@ namespace Barotrauma
|
||||
spawnPos = chosenPosition.Position.ToVector2();
|
||||
if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
|
||||
{
|
||||
var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, ruin: chosenPosition.Ruin, useSyncedRand: false);
|
||||
var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine, useSyncedRand: false);
|
||||
if (spawnPoint != null)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == (chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine));
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user