Release v0.15.12.0
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -42,7 +40,7 @@ namespace Barotrauma
|
||||
var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
|
||||
foreach (var target in targets)
|
||||
{
|
||||
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount, target.Position + Vector2.UnitY * 150.0f);
|
||||
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount);
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class GodModeAction : EventAction
|
||||
{
|
||||
[Serialize(true, true)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string TargetTag { get; set; }
|
||||
|
||||
public GodModeAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
var targets = ParentEvent.GetTargets(TargetTag);
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target != null && target is Character character)
|
||||
{
|
||||
character.GodMode = Enabled;
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(GodModeAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
|
||||
(Enabled ? "Enable godmode" : "Disable godmode");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace Barotrauma
|
||||
Faction faction = campaign.Factions.Find(faction1 => faction1.Prefab.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (faction != null)
|
||||
{
|
||||
faction.Reputation.Value += Increase;
|
||||
faction.Reputation.AddReputation(Increase);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -64,14 +64,14 @@ namespace Barotrauma
|
||||
Location location = campaign.Map.CurrentLocation;
|
||||
if (location != null)
|
||||
{
|
||||
location.Reputation.Value += Increase;
|
||||
location.Reputation.AddReputation(Increase);
|
||||
IEnumerable<Location> locations = location.Connections.SelectMany(c => c.Locations).Distinct().Where(l => l != null && l != location);
|
||||
foreach (Location connectedLocation in locations)
|
||||
{
|
||||
Debug.Assert(connectedLocation.Reputation != null, "connectedLocation.Reputation != null");
|
||||
if (connectedLocation.Reputation != null)
|
||||
{
|
||||
connectedLocation.Reputation.Value += (Increase / 4);
|
||||
connectedLocation.Reputation.AddReputation(Increase / 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Barotrauma
|
||||
{
|
||||
npcOrItem = npc;
|
||||
npc.CampaignInteractionType = CampaignMode.InteractionType.Examine;
|
||||
npc.RequireConsciousnessForCustomInteract = false;
|
||||
npc.RequireConsciousnessForCustomInteract = DisableIfTargetIncapacitated;
|
||||
#if CLIENT
|
||||
npc.SetCustomInteract(
|
||||
(speaker, player) => { if (e1 == speaker) { Trigger(speaker, player); } else { Trigger(player, speaker); } },
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma
|
||||
selectedEvents.Clear();
|
||||
activeEvents.Clear();
|
||||
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, indoorsSteering: false);
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
totalPathLength = 0.0f;
|
||||
if (level != null)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ namespace Barotrauma
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.prefab))
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.Prefabs))
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
@@ -380,9 +380,13 @@ namespace Barotrauma
|
||||
{
|
||||
pendingEventSets.Clear();
|
||||
selectedEvents.Clear();
|
||||
activeEvents.Clear();
|
||||
QueuedEvents.Clear();
|
||||
|
||||
preloadedSprites.ForEach(s => s.Remove());
|
||||
preloadedSprites.Clear();
|
||||
|
||||
pathFinder = null;
|
||||
}
|
||||
|
||||
private float CalculateCommonness(EventPrefab eventPrefab, float baseCommonness)
|
||||
@@ -430,23 +434,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var suitablePrefabs = eventSet.EventPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.prefab.BiomeIdentifier) ||
|
||||
e.prefab.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
bool isPrefabSuitable(EventPrefab p)
|
||||
=> string.IsNullOrEmpty(p.BiomeIdentifier) ||
|
||||
p.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var suitablePrefabSubsets = eventSet.EventPrefabs
|
||||
.FindAll(p => p.Prefabs.Any(isPrefabSuitable));
|
||||
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
if (suitablePrefabs.Count > 0)
|
||||
if (suitablePrefabSubsets.Count > 0)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(suitablePrefabs);
|
||||
var unusedEvents = suitablePrefabSubsets.ToList();
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => CalculateCommonness(e.prefab, e.commonness) <= 0.0f)) { break; }
|
||||
(EventPrefab eventPrefab, float commonness, float probability) = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e.prefab, e.commonness)).ToList(), rand);
|
||||
if (eventPrefab != null && rand.NextDouble() <= probability)
|
||||
if (unusedEvents.All(e => e.Prefabs.All(p => CalculateCommonness(p, e.Commonness) <= 0.0f))) { break; }
|
||||
EventSet.SubEventPrefab subEventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Prefabs.Max(p => CalculateCommonness(p, e.Commonness))).ToList(), rand);
|
||||
(IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) = subEventPrefab;
|
||||
if (eventPrefabs != null && rand.NextDouble() <= probability)
|
||||
{
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -461,7 +473,7 @@ namespace Barotrauma
|
||||
selectedEvents.Add(eventSet, new List<Event>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
unusedEvents.Remove((eventPrefab, commonness, probability));
|
||||
unusedEvents.Remove(subEventPrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,9 +488,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ((EventPrefab eventPrefab, float commonness, float probability) in suitablePrefabs)
|
||||
foreach ((IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) in suitablePrefabSubsets)
|
||||
{
|
||||
if (rand.NextDouble() > probability) { continue; }
|
||||
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -862,8 +878,9 @@ namespace Barotrauma
|
||||
|
||||
private float CalculateDistanceTraveled()
|
||||
{
|
||||
if (level == null) { return 0.0f; }
|
||||
if (level == null || pathFinder == null) { return 0.0f; }
|
||||
var refEntity = GetRefEntity();
|
||||
if (refEntity == null) { return 0.0f; }
|
||||
Vector2 target = ConvertUnits.ToSimUnits(level.EndPosition);
|
||||
var steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(refEntity.WorldPosition), target);
|
||||
if (steeringPath.Unreachable || float.IsPositiveInfinity(totalPathLength))
|
||||
@@ -976,6 +993,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
case SubmarineType.Wreck:
|
||||
case SubmarineType.BeaconStation:
|
||||
case SubmarineType.Ruin:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma
|
||||
BiomeIdentifier = ConfigElement.GetAttributeString("biome", string.Empty);
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
Probability = Math.Clamp(element.GetAttributeFloat(1.0f, "probability", "spawnprobability"), 0, 1);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", EventType != typeof(ScriptedEvent));
|
||||
|
||||
UnlockPathEvent = element.GetAttributeBool("unlockpathevent", false);
|
||||
UnlockPathTooltip = element.GetAttributeString("unlockpathtooltip", "lockedpathtooltip");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
@@ -48,10 +49,10 @@ namespace Barotrauma
|
||||
List<EventPrefab> eventPrefabs = new List<EventPrefab>(PrefabList);
|
||||
foreach (var eventSet in List)
|
||||
{
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
foreach (var childSet in eventSet.ChildSets)
|
||||
{
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
}
|
||||
}
|
||||
return eventPrefabs;
|
||||
@@ -98,7 +99,48 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
public readonly List<(EventPrefab prefab, float commonness, float probability)> EventPrefabs;
|
||||
public struct SubEventPrefab
|
||||
{
|
||||
public SubEventPrefab(string debugIdentifier, string[] prefabIdentifiers, float? commonness, float? probability)
|
||||
{
|
||||
EventPrefab tryFindPrefab(string id)
|
||||
{
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{id}\".");
|
||||
}
|
||||
return prefab;
|
||||
}
|
||||
|
||||
this.Prefabs = prefabIdentifiers
|
||||
.Select(tryFindPrefab)
|
||||
.Where(p => p != null)
|
||||
.ToImmutableArray();
|
||||
this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).Max();
|
||||
this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).Max();
|
||||
}
|
||||
|
||||
public SubEventPrefab(EventPrefab prefab, float commonness, float probability)
|
||||
{
|
||||
Prefabs = prefab.ToEnumerable().ToImmutableArray();
|
||||
Commonness = commonness;
|
||||
Probability = probability;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<EventPrefab> Prefabs;
|
||||
public readonly float Commonness;
|
||||
public readonly float Probability;
|
||||
|
||||
public void Deconstruct(out IEnumerable<EventPrefab> prefabs, out float commonness, out float probability)
|
||||
{
|
||||
prefabs = Prefabs;
|
||||
commonness = Commonness;
|
||||
probability = Probability;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly List<SubEventPrefab> EventPrefabs;
|
||||
|
||||
public readonly List<EventSet> ChildSets;
|
||||
|
||||
@@ -112,7 +154,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugIdentifier = element.GetAttributeString("identifier", null) ?? debugIdentifier;
|
||||
Commonness = new Dictionary<string, float>();
|
||||
EventPrefabs = new List<(EventPrefab prefab, float commonness, float probability)>();
|
||||
EventPrefabs = new List<SubEventPrefab>();
|
||||
ChildSets = new List<EventSet>();
|
||||
|
||||
BiomeIdentifier = element.GetAttributeString("biome", string.Empty);
|
||||
@@ -178,23 +220,20 @@ namespace Barotrauma
|
||||
//an element with just an identifier = reference to an event prefab
|
||||
if (!subElement.HasElements && subElement.Attributes().First().Name.ToString().Equals("identifier", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string identifier = subElement.GetAttributeString("identifier", "");
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{identifier}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
float commonness = subElement.GetAttributeFloat("commonness", prefab.Commonness);
|
||||
float probability = subElement.GetAttributeFloat("probability", prefab.Probability);
|
||||
EventPrefabs.Add((prefab, commonness, probability));
|
||||
}
|
||||
string[] identifiers = subElement.GetAttributeStringArray("identifier", Array.Empty<string>());
|
||||
|
||||
float commonness = subElement.GetAttributeFloat("commonness", -1f);
|
||||
float probability = subElement.GetAttributeFloat("probability", -1f);
|
||||
EventPrefabs.Add(new SubEventPrefab(
|
||||
debugIdentifier,
|
||||
identifiers,
|
||||
commonness>=0f ? commonness : (float?)null,
|
||||
probability>=0f ? probability : (float?)null));
|
||||
}
|
||||
else
|
||||
{
|
||||
var prefab = new EventPrefab(subElement);
|
||||
EventPrefabs.Add((prefab, prefab.Commonness, prefab.Probability));
|
||||
EventPrefabs.Add(new SubEventPrefab(prefab, prefab.Commonness, prefab.Probability));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -346,13 +385,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (thisSet.ChooseRandom)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(thisSet.EventPrefabs);
|
||||
var unusedEvents = thisSet.EventPrefabs.ToList();
|
||||
for (int i = 0; i < thisSet.EventCount; i++)
|
||||
{
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.prefab != null)
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.Prefabs.Any(p => p != null))
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
unusedEvents.Remove(eventPrefab);
|
||||
}
|
||||
}
|
||||
@@ -361,7 +400,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var eventPrefab in thisSet.EventPrefabs)
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
}
|
||||
}
|
||||
foreach (var childSet in thisSet.ChildSets)
|
||||
@@ -370,6 +409,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
static void AddEvents(EventDebugStats stats, IEnumerable<EventPrefab> eventPrefabs)
|
||||
=> eventPrefabs.ForEach(p => AddEvent(stats, p));
|
||||
|
||||
static void AddEvent(EventDebugStats stats, EventPrefab eventPrefab)
|
||||
{
|
||||
if (eventPrefab.EventType == typeof(MonsterEvent))
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace Barotrauma
|
||||
case 0:
|
||||
|
||||
if (items.All(it => it.Removed || it.Condition <= 0.0f) &&
|
||||
requireKill.All(c => c.Removed || c.IsDead) &&
|
||||
requireKill.All(c => c.Removed || c.IsDead || (c.LockHands && c.Submarine == Submarine.MainSub)) &&
|
||||
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
State = 1;
|
||||
|
||||
@@ -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 (State == 2)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
failed = !completed && State > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace Barotrauma
|
||||
int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
|
||||
@@ -140,6 +140,12 @@ namespace Barotrauma
|
||||
|
||||
public override int GetReward(Submarine sub)
|
||||
{
|
||||
// If we are not at the location of the mission, skip the calculation of the reward
|
||||
if (GameMain.GameSession?.StartLocation != Locations[0])
|
||||
{
|
||||
return calculatedReward;
|
||||
}
|
||||
|
||||
bool missionsChanged = false;
|
||||
if (GameMain.GameSession?.StartLocation?.SelectedMissions != null)
|
||||
{
|
||||
@@ -192,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
|
||||
|
||||
@@ -13,16 +13,5 @@ namespace Barotrauma
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
}
|
||||
#elif SERVER
|
||||
|
||||
public override void ServerWriteInitial(IWriteMessage msg, Client c)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace Barotrauma
|
||||
{
|
||||
partial class MineralMission : Mission
|
||||
{
|
||||
private Dictionary<string, Pair<int, float>> ResourceClusters { get; } = new Dictionary<string, Pair<int, float>>();
|
||||
private Dictionary<string, List<Item>> SpawnedResources { get; } = new Dictionary<string, List<Item>>();
|
||||
private Dictionary<string, Item[]> RelevantLevelResources { get; } = new Dictionary<string, Item[]>();
|
||||
private List<Tuple<string, Vector2>> MissionClusterPositions { get; } = new List<Tuple<string, Vector2>>();
|
||||
private readonly Dictionary<string, (int amount, float rotation)> resourceClusters = new Dictionary<string, (int amount, float rotation)>();
|
||||
private readonly Dictionary<string, List<Item>> spawnedResources = new Dictionary<string, List<Item>>();
|
||||
private readonly Dictionary<string, Item[]> relevantLevelResources = new Dictionary<string, Item[]>();
|
||||
private readonly List<Tuple<string, Vector2>> missionClusterPositions = new List<Tuple<string, Vector2>>();
|
||||
|
||||
private readonly HashSet<Level.Cave> caves = new HashSet<Level.Cave>();
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return MissionClusterPositions
|
||||
.Where(p => SpawnedResources.ContainsKey(p.Item1) && AnyAreUncollected(SpawnedResources[p.Item1]))
|
||||
return missionClusterPositions
|
||||
.Where(p => spawnedResources.ContainsKey(p.Item1) && AnyAreUncollected(spawnedResources[p.Item1]))
|
||||
.Select(p => p.Item2);
|
||||
}
|
||||
}
|
||||
@@ -33,53 +33,53 @@ namespace Barotrauma
|
||||
{
|
||||
var identifier = c.GetAttributeString("identifier", null);
|
||||
if (string.IsNullOrWhiteSpace(identifier)) { continue; }
|
||||
if (ResourceClusters.ContainsKey(identifier))
|
||||
if (resourceClusters.ContainsKey(identifier))
|
||||
{
|
||||
ResourceClusters[identifier].First++;
|
||||
resourceClusters[identifier] = (resourceClusters[identifier].amount + 1, resourceClusters[identifier].rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourceClusters.Add(identifier, new Pair<int, float>(1, 0.0f));
|
||||
resourceClusters.Add(identifier, (1, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (SpawnedResources.Any())
|
||||
if (spawnedResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({SpawnedResources.Count})");
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({spawnedResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Spawned resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
SpawnedResources.Clear();
|
||||
spawnedResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (RelevantLevelResources.Any())
|
||||
if (relevantLevelResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({RelevantLevelResources.Count})");
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({relevantLevelResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Relevant level resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
RelevantLevelResources.Clear();
|
||||
relevantLevelResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (MissionClusterPositions.Any())
|
||||
if (missionClusterPositions.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({MissionClusterPositions.Count})");
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({missionClusterPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Mission cluster positions list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
MissionClusterPositions.Clear();
|
||||
missionClusterPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
caves.Clear();
|
||||
|
||||
if (IsClient) { return; }
|
||||
foreach (var kvp in ResourceClusters)
|
||||
foreach (var kvp in resourceClusters)
|
||||
{
|
||||
var prefab = ItemPrefab.Find(null, kvp.Key);
|
||||
if (prefab == null)
|
||||
@@ -88,15 +88,14 @@ namespace Barotrauma
|
||||
"couldn't find an item prefab with the identifier " + kvp.Key);
|
||||
continue;
|
||||
}
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.First, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.First)
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.amount, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.amount)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.First + " of " + prefab.Name);
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.amount + " of " + prefab.Name);
|
||||
}
|
||||
if (spawnedResources.None()) { continue; }
|
||||
SpawnedResources.Add(kvp.Key, spawnedResources);
|
||||
kvp.Value.Second = rotation;
|
||||
this.spawnedResources.Add(kvp.Key, spawnedResources);
|
||||
|
||||
foreach (Level.Cave cave in Level.Loaded.Caves)
|
||||
{
|
||||
@@ -142,7 +141,7 @@ namespace Barotrauma
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var kvp in SpawnedResources)
|
||||
foreach (var kvp in spawnedResources)
|
||||
{
|
||||
foreach (var i in kvp.Value)
|
||||
{
|
||||
@@ -152,33 +151,33 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnedResources.Clear();
|
||||
RelevantLevelResources.Clear();
|
||||
MissionClusterPositions.Clear();
|
||||
spawnedResources.Clear();
|
||||
relevantLevelResources.Clear();
|
||||
missionClusterPositions.Clear();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
|
||||
private void FindRelevantLevelResources()
|
||||
{
|
||||
RelevantLevelResources.Clear();
|
||||
foreach (var identifier in ResourceClusters.Keys)
|
||||
relevantLevelResources.Clear();
|
||||
foreach (var identifier in resourceClusters.Keys)
|
||||
{
|
||||
var items = Item.ItemList.Where(i => i.Prefab.Identifier == identifier &&
|
||||
i.Submarine == null && i.ParentInventory == null &&
|
||||
(!(i.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)))
|
||||
.ToArray();
|
||||
RelevantLevelResources.Add(identifier, items);
|
||||
relevantLevelResources.Add(identifier, items);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnoughHaveBeenCollected()
|
||||
{
|
||||
foreach (var kvp in ResourceClusters)
|
||||
foreach (var kvp in resourceClusters)
|
||||
{
|
||||
if (RelevantLevelResources.TryGetValue(kvp.Key, out var availableResources))
|
||||
if (relevantLevelResources.TryGetValue(kvp.Key, out var availableResources))
|
||||
{
|
||||
var collected = availableResources.Count(r => HasBeenCollected(r));
|
||||
var needed = kvp.Value.First;
|
||||
var needed = kvp.Value.amount;
|
||||
if (collected < needed) { return false; }
|
||||
}
|
||||
else
|
||||
@@ -210,8 +209,8 @@ namespace Barotrauma
|
||||
|
||||
private void CalculateMissionClusterPositions()
|
||||
{
|
||||
MissionClusterPositions.Clear();
|
||||
foreach (var kvp in SpawnedResources)
|
||||
missionClusterPositions.Clear();
|
||||
foreach (var kvp in spawnedResources)
|
||||
{
|
||||
if (kvp.Value.None()) { continue; }
|
||||
var pos = Vector2.Zero;
|
||||
@@ -222,7 +221,7 @@ namespace Barotrauma
|
||||
itemCount++;
|
||||
}
|
||||
pos /= itemCount;
|
||||
MissionClusterPositions.Add(new Tuple<string, Vector2>(kvp.Key, pos));
|
||||
missionClusterPositions.Add(new Tuple<string, Vector2>(kvp.Key, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Abilities;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -25,7 +27,7 @@ namespace Barotrauma
|
||||
state = value;
|
||||
TryTriggerEvents(state);
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(this, state);
|
||||
GameMain.Server?.UpdateMissionState(this);
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
}
|
||||
@@ -343,19 +345,57 @@ namespace Barotrauma
|
||||
public void GiveReward()
|
||||
{
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode campaign)) { return; }
|
||||
campaign.Money += GetReward(Submarine.MainSub);
|
||||
int reward = GetReward(Submarine.MainSub);
|
||||
|
||||
float baseExperienceGain = reward * 0.09f;
|
||||
|
||||
float difficultyMultiplier = 1 + level.Difficulty / 100f;
|
||||
baseExperienceGain *= difficultyMultiplier;
|
||||
|
||||
IEnumerable<Character> crewCharacters = GameSession.GetSessionCrewCharacters();
|
||||
|
||||
// use multipliers here so that we can easily add them together without introducing multiplicative XP stacking
|
||||
var experienceGainMultiplier = new AbilityValue(1f);
|
||||
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnAllyGainMissionExperience, experienceGainMultiplier));
|
||||
crewCharacters.ForEach(c => experienceGainMultiplier.Value += c.GetStatValue(StatTypes.MissionExperienceGainMultiplier));
|
||||
|
||||
int experienceGain = (int)(baseExperienceGain * experienceGainMultiplier.Value);
|
||||
#if CLIENT
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
character.Info?.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#else
|
||||
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
//give the experience to the stored characterinfo if the client isn't currently controlling a character
|
||||
(c.Character?.Info ?? c.CharacterInfo)?.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// apply money gains afterwards to prevent them from affecting XP gains
|
||||
var moneyGainMission = new AbilityValueMission(1f, this);
|
||||
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, moneyGainMission));
|
||||
crewCharacters.ForEach(c => moneyGainMission.Value += c.GetStatValue(StatTypes.MissionMoneyGainMultiplier));
|
||||
|
||||
campaign.Money += (int)(reward * moneyGainMission.Value);
|
||||
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
character.Info.MissionsCompletedSinceDeath++;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, float> reputationReward in ReputationRewards)
|
||||
{
|
||||
if (reputationReward.Key.Equals("location", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Locations[0].Reputation.Value += reputationReward.Value;
|
||||
Locations[1].Reputation.Value += reputationReward.Value;
|
||||
Locations[0].Reputation.AddReputation(reputationReward.Value);
|
||||
Locations[1].Reputation.AddReputation(reputationReward.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Faction faction = campaign.Factions.Find(faction1 => faction1.Prefab.Identifier.Equals(reputationReward.Key, StringComparison.OrdinalIgnoreCase));
|
||||
if (faction != null) { faction.Reputation.Value += reputationReward.Value; }
|
||||
if (faction != null) { faction.Reputation.AddReputation(reputationReward.Value); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,5 +483,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;
|
||||
}
|
||||
|
||||
@@ -180,7 +180,11 @@ namespace Barotrauma
|
||||
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(patrolPos), ConvertUnits.ToSimUnits(preferredSpawnPos));
|
||||
if (!path.Unreachable)
|
||||
{
|
||||
preferredSpawnPos = path.Nodes[Rand.Range(0, path.Nodes.Count - 1)].WorldPosition; // spawn the sub in a random point in the path if possible
|
||||
var validNodes = path.Nodes.FindAll(n => !Level.Loaded.ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(n.WorldPosition))));
|
||||
if (validNodes.Any())
|
||||
{
|
||||
preferredSpawnPos = validNodes.GetRandom().WorldPosition; // spawn the sub in a random point in the path if possible
|
||||
}
|
||||
}
|
||||
|
||||
int graceDistance = 500; // the sub still spawns awkwardly close to walls, so this helps. could also be given as a parameter instead
|
||||
@@ -382,11 +386,11 @@ namespace Barotrauma
|
||||
State = newState;
|
||||
}
|
||||
|
||||
private bool CheckWinState() => !IsClient && (characters.All(m => !Survived(m)));
|
||||
private bool CheckWinState() => !IsClient && characters.All(m => DeadOrCaptured(m));
|
||||
|
||||
private bool Survived(Character character)
|
||||
private bool DeadOrCaptured(Character character)
|
||||
{
|
||||
return character != null && !character.Removed && !character.IsDead;
|
||||
return character != null && !character.Removed && (character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub));
|
||||
}
|
||||
|
||||
public override void End()
|
||||
|
||||
@@ -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; }
|
||||
@@ -247,8 +247,8 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
var root = item.GetRootContainer() ?? item;
|
||||
if (root.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
var root = item?.GetRootContainer() ?? item;
|
||||
if (root?.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
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;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 ruinWaypoints = TargetRuin.Submarine.GetWaypoints(false);
|
||||
ruinWaypoints.RemoveAll(wp => wp.CurrentHull == null);
|
||||
if (ruinWaypoints.Count < targetsToScan)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({ruinWaypoints.Count} < {targetsToScan})");
|
||||
return;
|
||||
}
|
||||
var availableWaypoints = new List<WayPoint>();
|
||||
float minTargetDistanceSquared = minTargetDistance * minTargetDistance;
|
||||
for (int tries = 0; tries < 15; tries++)
|
||||
{
|
||||
scanTargets.Clear();
|
||||
availableWaypoints.Clear();
|
||||
availableWaypoints.AddRange(ruinWaypoints);
|
||||
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())
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available on try #{tries + 1} to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scanTargets.Count >= targetsToScan)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"Successfully initialized a Scan mission: targets set on try #{tries + 1}", Color.Green);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if ((tries + 1) % 5 == 0)
|
||||
{
|
||||
float reducedMinTargetDistance = (1.0f - (((tries + 1) / 5) * 0.1f)) * minTargetDistance;
|
||||
minTargetDistanceSquared = reducedMinTargetDistance * reducedMinTargetDistance;
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"Reducing minimum distance between Scan mission targets (new min: {reducedMinTargetDistance}) to reach the required target count", Color.Yellow);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (scanTargets.Count < targetsToScan)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
|
||||
}
|
||||
}
|
||||
|
||||
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 (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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,11 @@ namespace Barotrauma
|
||||
private bool disallowed;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
private readonly string spawnPointTag;
|
||||
|
||||
private bool spawnPending;
|
||||
|
||||
private int maxAmountPerLevel = int.MaxValue;
|
||||
private readonly int maxAmountPerLevel = int.MaxValue;
|
||||
|
||||
public List<Character> Monsters => monsters;
|
||||
public Vector2? SpawnPos => spawnPos;
|
||||
@@ -87,6 +88,8 @@ namespace Barotrauma
|
||||
spawnPosType = Level.PositionType.Abyss;
|
||||
}
|
||||
|
||||
spawnPointTag = prefab.ConfigElement.GetAttributeString("spawnpointtag", string.Empty);
|
||||
|
||||
offset = prefab.ConfigElement.GetAttributeFloat("offset", 0);
|
||||
scatter = Math.Clamp(prefab.ConfigElement.GetAttributeFloat("scatter", 500), 0, 3000);
|
||||
|
||||
@@ -285,19 +288,19 @@ 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);
|
||||
if (spawnPoint != null)
|
||||
var spawnPoint =
|
||||
WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine, useSyncedRand: false, spawnPointTag: spawnPointTag);
|
||||
if (spawnPoint != null)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == (chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine));
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath)
|
||||
@@ -448,7 +451,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
string seed = Level.Loaded.Seed + i.ToString();
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
|
||||
Reference in New Issue
Block a user