Unstable 0.15.15.0 (and the one before it I forgor)

This commit is contained in:
Markus Isberg
2021-11-18 21:34:30 +09:00
parent 10e5fd5f3e
commit 80f39cd2a3
257 changed files with 4916 additions and 2582 deletions
@@ -121,7 +121,7 @@ namespace Barotrauma
{
foreach (Item item in newCharacter.Inventory.AllItems)
{
item.SpawnedInOutpost = true;
item.SpawnedInCurrentOutpost = true;
item.AllowStealing = false;
}
}
@@ -33,6 +33,8 @@ namespace Barotrauma
private float currentIntensity;
//The exact intensity of the current situation, current intensity is lerped towards this value
private float targetIntensity;
//follows targetIntensity a bit faster than currentIntensity to prevent e.g. combat musing staying on very long after the monsters are dead
private float musicIntensity;
//How low the intensity has to be for an event to be triggered.
//Gradually increases with time, so additional problems can still appear eventually even if
@@ -50,7 +52,11 @@ namespace Barotrauma
private float calculateDistanceTraveledTimer;
private float distanceTraveled;
private float avgCrewHealth, avgHullIntegrity, floodingAmount, fireAmount, enemyDanger, monsterTotalStrength;
private float avgCrewHealth, avgHullIntegrity, floodingAmount, fireAmount, enemyDanger, monsterStrength;
public float CumulativeMonsterStrengthMain;
public float CumulativeMonsterStrengthRuins;
public float CumulativeMonsterStrengthWrecks;
public float CumulativeMonsterStrengthCaves;
private float roundDuration;
@@ -78,6 +84,10 @@ namespace Barotrauma
{
get { return currentIntensity; }
}
public float MusicIntensity
{
get { return musicIntensity; }
}
public List<Event> ActiveEvents
{
@@ -85,7 +95,22 @@ namespace Barotrauma
}
public readonly Queue<Event> QueuedEvents = new Queue<Event>();
private struct TimeStamp
{
public readonly double Time;
public readonly Event Event;
public TimeStamp(Event e)
{
Event = e;
Time = Timing.TotalTime;
}
}
private readonly List<TimeStamp> timeStamps = new List<TimeStamp>();
public void AddTimeStamp(Event e) => timeStamps.Add(new TimeStamp(e));
public EventManager()
{
isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
@@ -99,6 +124,7 @@ namespace Barotrauma
if (isClient) { return; }
timeStamps.Clear();
pendingEventSets.Clear();
selectedEvents.Clear();
activeEvents.Clear();
@@ -202,8 +228,12 @@ namespace Barotrauma
crewAwayResetTimer = 0.0f;
intensityUpdateTimer = 0.0f;
CalculateCurrentIntensity(0.0f);
currentIntensity = targetIntensity;
currentIntensity = musicIntensity = targetIntensity;
eventCoolDown = 0.0f;
CumulativeMonsterStrengthMain = 0;
CumulativeMonsterStrengthRuins = 0;
CumulativeMonsterStrengthWrecks = 0;
CumulativeMonsterStrengthCaves = 0;
}
private void SelectSettings()
@@ -401,11 +431,7 @@ namespace Barotrauma
{
if (level == null) { return; }
if (level.LevelData.HasHuntingGrounds && eventSet.DisableInHuntingGrounds) { return; }
#if DEBUG
DebugConsole.NewMessage($"Loading event set {eventSet.DebugIdentifier}", Color.LightBlue);
#else
DebugConsole.Log($"Loading event set {eventSet.DebugIdentifier}");
#endif
DebugConsole.NewMessage($"Loading event set {eventSet.DebugIdentifier}", Color.LightBlue, debugOnly: true);
int applyCount = 1;
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
if (eventSet.PerRuin)
@@ -413,7 +439,7 @@ namespace Barotrauma
applyCount = level.Ruins.Count();
foreach (var ruin in level.Ruins)
{
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Ruin == ruin; });
spawnPosFilter.Add(pos => pos.Ruin == ruin);
}
}
else if (eventSet.PerCave)
@@ -421,7 +447,7 @@ namespace Barotrauma
applyCount = level.Caves.Count();
foreach (var cave in level.Caves)
{
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Cave == cave; });
spawnPosFilter.Add(pos => pos.Cave == cave);
}
}
else if (eventSet.PerWreck)
@@ -430,7 +456,7 @@ namespace Barotrauma
applyCount = wrecks.Count();
foreach (var wreck in wrecks)
{
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Submarine == wreck; });
spawnPosFilter.Add(pos => pos.Submarine == wreck);
}
}
@@ -463,11 +489,7 @@ namespace Barotrauma
if (newEvent == null) { continue; }
newEvent.Init(true);
if (i < spawnPosFilter.Count) { newEvent.SpawnPosFilter = spawnPosFilter[i]; }
#if DEBUG
DebugConsole.NewMessage($"Initialized event {newEvent}");
#else
DebugConsole.Log($"Initialized event {newEvent}");
#endif
DebugConsole.NewMessage($"Initialized event {newEvent}", debugOnly: true);
if (!selectedEvents.ContainsKey(eventSet))
{
selectedEvents.Add(eventSet, new List<Event>());
@@ -498,11 +520,7 @@ namespace Barotrauma
var newEvent = eventPrefab.CreateInstance();
if (newEvent == null) { continue; }
newEvent.Init(true);
#if DEBUG
DebugConsole.NewMessage($"Initialized event {newEvent}");
#else
DebugConsole.Log($"Initialized event {newEvent}");
#endif
DebugConsole.NewMessage($"Initialized event {newEvent}", debugOnly: true);
if (!selectedEvents.ContainsKey(eventSet))
{
selectedEvents.Add(eventSet, new List<Event>());
@@ -525,6 +543,7 @@ namespace Barotrauma
var allowedEventSets =
eventSets.Where(es =>
es.IsCampaignSet == GameMain.GameSession?.GameMode is CampaignMode &&
level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty &&
level.LevelData.Type == es.LevelType &&
(string.IsNullOrEmpty(es.BiomeIdentifier) || es.BiomeIdentifier.Equals(level.LevelData.Biome.Identifier, StringComparison.OrdinalIgnoreCase)));
@@ -647,6 +666,7 @@ namespace Barotrauma
isCrewAway = false;
crewAwayDuration = 0.0f;
eventThreshold += settings.EventThresholdIncrease * deltaTime;
eventThreshold = Math.Min(eventThreshold, 1.0f);
eventCoolDown -= deltaTime;
}
@@ -739,7 +759,7 @@ namespace Barotrauma
// enemy amount --------------------------------------------------------
enemyDanger = 0.0f;
monsterTotalStrength = 0;
monsterStrength = 0;
foreach (Character character in Character.CharacterList)
{
if (character.IsIncapacitated || !character.Enabled || character.IsPet || character.Params.CompareGroup("human")) { continue; }
@@ -749,28 +769,9 @@ namespace Barotrauma
if (!enemyAI.AIParams.StayInAbyss)
{
// Ignore abyss monsters because they can stay active for quite great distances. They'll be taken into account when they target the sub.
monsterTotalStrength += enemyAI.CombatStrength;
monsterStrength += enemyAI.CombatStrength;
}
// Example combat strengths:
// Hammerheadspawn 1
// Moloch Pupa 1
// Terminal cell 20
// Leucocyte 40
// Husk 90
// Crawler 100
// Unarmored Mudraptor 140
// Spineling 150
// Tigerthresher 200
// Armored Mudraptor 210
// Watcher 400
// Golden Hammerhead 400
// Hammerhead 500
// Hammerhead Matriarch 550
// Bonethresher 600
// Moloch 1250
// Black Moloch 1500
// Endworm 10000
if (character.CurrentHull?.Submarine != null &&
(character.CurrentHull.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(character.CurrentHull.Submarine)))
{
@@ -792,7 +793,7 @@ namespace Barotrauma
// 5 Mudraptors -> +0.21 (0.42 in total, before they get inside).
// 3 Hammerheads -> +0.3 (0.6 in total, if they all target the sub).
// 2 Molochs -> +0.5 (1.0 in total, if both target the sub).
enemyDanger += monsterTotalStrength / 5000f;
enemyDanger += monsterStrength / 5000f;
enemyDanger = MathHelper.Clamp(enemyDanger, 0.0f, 1.0f);
// The definitions above aim for that we never spawn more monsters that the player (and the performance) can handle.
@@ -868,11 +869,15 @@ namespace Barotrauma
{
//25 seconds for intensity to go from 0.0 to 1.0
currentIntensity = Math.Min(currentIntensity + 0.04f * IntensityUpdateInterval, targetIntensity);
//20 seconds for intensity to go from 0.0 to 1.0
musicIntensity = Math.Min(musicIntensity + 0.05f * IntensityUpdateInterval, targetIntensity);
}
else
{
//400 seconds for intensity to go from 1.0 to 0.0
currentIntensity = Math.Max(currentIntensity - 0.0025f * IntensityUpdateInterval, targetIntensity);
//20 seconds for intensity to go from 1.0 to 0.0
musicIntensity = Math.Max(musicIntensity - 0.05f * IntensityUpdateInterval, targetIntensity);
}
}
@@ -13,6 +13,7 @@ namespace Barotrauma
public float Commonness;
public string Identifier;
public string BiomeIdentifier;
public float SpawnDistance;
public bool UnlockPathEvent;
public string UnlockPathTooltip;
@@ -46,25 +47,30 @@ namespace Barotrauma
UnlockPathTooltip = element.GetAttributeString("unlockpathtooltip", "lockedpathtooltip");
UnlockPathReputation = element.GetAttributeInt("unlockpathreputation", 0);
UnlockPathFaction = element.GetAttributeString("unlockpathfaction", "");
SpawnDistance = element.GetAttributeFloat("spawndistance", 0);
}
public bool TryCreateInstance<T>(out T instance) where T : Event
{
instance = CreateInstance() as T;
return instance is T;
}
public Event CreateInstance()
{
ConstructorInfo constructor = EventType.GetConstructor(new[] { typeof(EventPrefab) });
object instance = null;
Event instance = null;
try
{
instance = constructor.Invoke(new object[] { this });
instance = constructor.Invoke(new object[] { this }) as Event;
}
catch (Exception ex)
{
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
}
Event ev = (Event)instance;
if (!ev.LevelMeetsRequirements()) { return null; }
return (Event)instance;
if (instance != null && !instance.LevelMeetsRequirements()) { return null; }
return instance;
}
public override string ToString()
@@ -14,6 +14,7 @@ namespace Barotrauma
{
public readonly EventSet RootSet;
public readonly Dictionary<string, int> MonsterCounts = new Dictionary<string, int>();
public float MonsterStrength;
public EventDebugStats(EventSet rootSet)
{
@@ -63,6 +64,8 @@ namespace Barotrauma
return GetAllEventPrefabs().Find(prefab => string.Equals(prefab.Identifier, identifer, StringComparison.Ordinal));
}
public readonly bool IsCampaignSet;
//0-100
public readonly float MinLevelDifficulty, MaxLevelDifficulty;
@@ -193,6 +196,7 @@ namespace Barotrauma
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
OncePerOutpost = element.GetAttributeBool("onceperoutpost", false);
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
IsCampaignSet = element.GetAttributeBool("campaign", LevelType == LevelData.LevelType.Outpost);
Commonness[""] = element.GetAttributeFloat("commonness", 1.0f);
foreach (XElement subElement in element.Elements())
@@ -205,7 +209,7 @@ namespace Barotrauma
{
if (overrideElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase))
{
string levelType = overrideElement.GetAttributeString("leveltype", "");
string levelType = overrideElement.GetAttributeString("leveltype", "").ToLowerInvariant();
if (!Commonness.ContainsKey(levelType))
{
Commonness.Add(levelType, overrideElement.GetAttributeFloat("commonness", 0.0f));
@@ -227,8 +231,8 @@ namespace Barotrauma
EventPrefabs.Add(new SubEventPrefab(
debugIdentifier,
identifiers,
commonness>=0f ? commonness : (float?)null,
probability>=0f ? probability : (float?)null));
commonness >= 0f ? commonness : (float?)null,
probability >= 0f ? probability : (float?)null));
}
else
{
@@ -347,7 +351,7 @@ namespace Barotrauma
}
}
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100)
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100, Func<MonsterEvent, bool> filter = null)
{
List<string> debugLines = new List<string>();
@@ -357,82 +361,75 @@ namespace Barotrauma
for (int i = 0; i < simulatedRoundCount; i++)
{
var newStats = new EventDebugStats(eventSet);
CheckEventSet(newStats, eventSet);
CheckEventSet(newStats, eventSet, filter);
stats.Add(newStats);
}
debugLines.Add($"Event stats ({eventSet.DebugIdentifier}): ");
LogEventStats(stats, debugLines);
}
for (int difficulty = 0; difficulty <= 100; difficulty += 10)
{
debugLines.Add($"Event stats on difficulty level {difficulty}: ");
List<EventDebugStats> stats = new List<EventDebugStats>();
for (int i = 0; i < simulatedRoundCount; i++)
{
EventSet selectedSet = List.Where(s => difficulty >= s.MinLevelDifficulty && difficulty <= s.MaxLevelDifficulty).GetRandom();
if (selectedSet == null) { continue; }
var newStats = new EventDebugStats(selectedSet);
CheckEventSet(newStats, selectedSet);
stats.Add(newStats);
}
LogEventStats(stats, debugLines);
}
return debugLines;
static void CheckEventSet(EventDebugStats stats, EventSet thisSet)
static void CheckEventSet(EventDebugStats stats, EventSet thisSet, Func<MonsterEvent, bool> filter = null)
{
if (thisSet.ChooseRandom)
{
var unusedEvents = thisSet.EventPrefabs.ToList();
for (int i = 0; i < thisSet.EventCount; i++)
if (unusedEvents.Any())
{
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
if (eventPrefab.Prefabs.Any(p => p != null))
for (int i = 0; i < thisSet.EventCount; i++)
{
AddEvents(stats, eventPrefab.Prefabs);
unusedEvents.Remove(eventPrefab);
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList());
if (eventPrefab.Prefabs.Any(p => p != null))
{
AddEvents(stats, eventPrefab.Prefabs, filter);
unusedEvents.Remove(eventPrefab);
}
}
}
List<float> values = thisSet.ChildSets.SelectMany(s => s.Commonness.Values).ToList();
EventSet childSet = ToolBox.SelectWeightedRandom(thisSet.ChildSets, values);
if (childSet != null)
{
CheckEventSet(stats, childSet, filter);
}
}
else
{
foreach (var eventPrefab in thisSet.EventPrefabs)
{
AddEvents(stats, eventPrefab.Prefabs);
AddEvents(stats, eventPrefab.Prefabs, filter);
}
foreach (var childSet in thisSet.ChildSets)
{
CheckEventSet(stats, childSet, filter);
}
}
foreach (var childSet in thisSet.ChildSets)
{
CheckEventSet(stats, childSet);
}
}
static void AddEvents(EventDebugStats stats, IEnumerable<EventPrefab> eventPrefabs)
=> eventPrefabs.ForEach(p => AddEvent(stats, p));
static void AddEvents(EventDebugStats stats, IEnumerable<EventPrefab> eventPrefabs, Func<MonsterEvent, bool> filter = null)
=> eventPrefabs.ForEach(p => AddEvent(stats, p, filter));
static void AddEvent(EventDebugStats stats, EventPrefab eventPrefab)
static void AddEvent(EventDebugStats stats, EventPrefab eventPrefab, Func<MonsterEvent, bool> filter = null)
{
if (eventPrefab.EventType == typeof(MonsterEvent))
if (eventPrefab.EventType == typeof(MonsterEvent) && eventPrefab.TryCreateInstance(out MonsterEvent monsterEvent))
{
float spawnProbability = eventPrefab.ConfigElement.GetAttributeFloat("spawnprobability", 1.0f);
if (Rand.Value(Rand.RandSync.Server) > spawnProbability)
{
return;
}
if (filter != null && !filter(monsterEvent)) { return; }
string character = eventPrefab.ConfigElement.GetAttributeString("characterfile", "");
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(character));
int amount = eventPrefab.ConfigElement.GetAttributeInt("amount", 0);
int minAmount = eventPrefab.ConfigElement.GetAttributeInt("minamount", amount);
int maxAmount = eventPrefab.ConfigElement.GetAttributeInt("maxamount", amount);
float spawnProbability = monsterEvent.Prefab.Probability;
if (Rand.Value() > spawnProbability) { return; }
int count = Rand.Range(minAmount, maxAmount + 1);
string character = monsterEvent.speciesName;
int count = Rand.Range(monsterEvent.MinAmount, monsterEvent.MaxAmount + 1);
if (count <= 0) { return; }
if (!stats.MonsterCounts.ContainsKey(character)) { stats.MonsterCounts[character] = 0; }
stats.MonsterCounts[character] += count;
var aiElement = CharacterPrefab.FindBySpeciesName(character)?.XDocument?.Root?.GetChildElement("ai");
if (aiElement != null)
{
stats.MonsterStrength += aiElement.GetAttributeFloat("combatstrength", 0) * count;
}
}
}
@@ -445,16 +442,21 @@ namespace Barotrauma
}
else
{
stats.Sort((s1, s2) => { return s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()); });
EventDebugStats minStats = stats.First();
EventDebugStats maxStats = stats.First();
debugLines.Add($" Minimum monster spawns: {stats.First().MonsterCounts.Values.Sum()}");
stats.Sort((s1, s2) => s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()));
debugLines.Add($" Minimum monster count: {stats.First().MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats.First())}");
debugLines.Add($" Median monster spawns: {stats[stats.Count / 2].MonsterCounts.Values.Sum()}");
debugLines.Add($" Median monster count: {stats[stats.Count / 2].MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats[stats.Count / 2])}");
debugLines.Add($" Maximum monster spawns: {stats.Last().MonsterCounts.Values.Sum()}");
debugLines.Add($" Maximum monster count: {stats.Last().MonsterCounts.Values.Sum()}");
debugLines.Add($" {LogMonsterCounts(stats.Last())}");
debugLines.Add($" Average monster count: {StringFormatter.FormatZeroDecimal((float)stats.Average(s => s.MonsterCounts.Values.Sum()))}");
debugLines.Add($" ");
stats.Sort((s1, s2) => s1.MonsterStrength.CompareTo(s2.MonsterStrength));
debugLines.Add($" Minimum monster strength: {StringFormatter.FormatZeroDecimal(stats.First().MonsterStrength)}");
debugLines.Add($" Median monster strength: {StringFormatter.FormatZeroDecimal(stats[stats.Count / 2].MonsterStrength)}");
debugLines.Add($" Maximum monster strength: {StringFormatter.FormatZeroDecimal(stats.Last().MonsterStrength)}");
debugLines.Add($" Average monster strength: {StringFormatter.FormatZeroDecimal(stats.Average(s => s.MonsterStrength))}");
debugLines.Add($" ");
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -161,6 +162,24 @@ namespace Barotrauma
#endif
}
}
//if any of the target items is a reactor, prevent exploding it from damaging the player's sub
foreach (Item item in items)
{
if (item.GetComponent<Reactor>() is Reactor reactor && (reactor.statusEffectLists?.ContainsKey(ActionType.OnBroken) ?? false))
{
foreach (var statusEffect in reactor.statusEffectLists[ActionType.OnBroken])
{
foreach (Explosion explosion in statusEffect.Explosions)
{
foreach (Submarine sub in Submarine.Loaded)
{
if (sub.TeamID == CharacterTeamType.Team1) { explosion.IgnoredSubmarines.Add(sub); }
}
}
}
}
}
}
private void InitCharacters(Submarine submarine)
@@ -207,7 +207,7 @@ namespace Barotrauma
var item = new Item(itemPrefab, position.Value, cargoRoomSub)
{
SpawnedInOutpost = true,
SpawnedInCurrentOutpost = true,
AllowStealing = false
};
item.FindHull();
@@ -1,4 +1,3 @@
using Barotrauma.Extensions;
using System.Collections.Generic;
namespace Barotrauma
@@ -6,8 +5,6 @@ namespace Barotrauma
partial class CombatMission : Mission
{
private Submarine[] subs;
// TODO: not used
private List<Character>[] crews;
private readonly string[] descriptions;
private static string[] teamNames = { "Team A", "Team B" };
@@ -103,15 +100,16 @@ namespace Barotrauma
subs[0].NeutralizeBallast();
subs[0].TeamID = CharacterTeamType.Team1;
subs[0].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team1);
subs[0].GetConnectedSubs().ForEach(s => s.TeamID = CharacterTeamType.Team1);
subs[1].NeutralizeBallast();
subs[1].TeamID = CharacterTeamType.Team2;
subs[1].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team2);
subs[1].GetConnectedSubs().ForEach(s => s.TeamID = CharacterTeamType.Team2);
subs[1].SetPosition(subs[1].FindSpawnPos(Level.Loaded.EndPosition));
subs[1].FlipX();
#if SERVER
crews = new List<Character>[] { new List<Character>(), new List<Character>() };
#endif
}
public override void End()
@@ -203,6 +203,14 @@ namespace Barotrauma
enemySub.TeamID = CharacterTeamType.None;
//make the enemy sub withstand atleast the same depth as the player sub
enemySub.RealWorldCrushDepth = Math.Max(enemySub.RealWorldCrushDepth, Submarine.MainSub.RealWorldCrushDepth);
if (Level.Loaded != null)
{
//...and the depth of the patrol positions + 1000 m
foreach (var patrolPos in patrolPositions)
{
enemySub.RealWorldCrushDepth = Math.Max(enemySub.RealWorldCrushDepth, Level.Loaded.GetRealWorldDepth(patrolPos.Y) + 1000);
}
}
enemySub.ImmuneToBallastFlora = true;
}
@@ -9,8 +9,8 @@ namespace Barotrauma
{
class MonsterEvent : Event
{
private readonly string speciesName;
private readonly int minAmount, maxAmount;
public readonly string speciesName;
public readonly int minAmount, maxAmount;
private List<Character> monsters;
private readonly float scatter;
@@ -20,7 +20,7 @@ namespace Barotrauma
private bool disallowed;
private readonly Level.PositionType spawnPosType;
public readonly Level.PositionType SpawnPosType;
private readonly string spawnPointTag;
private bool spawnPending;
@@ -77,15 +77,15 @@ namespace Barotrauma
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
!Enum.TryParse(spawnPosTypeStr, true, out spawnPosType))
!Enum.TryParse(spawnPosTypeStr, true, out SpawnPosType))
{
spawnPosType = Level.PositionType.MainPath;
SpawnPosType = Level.PositionType.MainPath;
}
//backwards compatibility
if (prefab.ConfigElement.GetAttributeBool("spawndeep", false))
{
spawnPosType = Level.PositionType.Abyss;
SpawnPosType = Level.PositionType.Abyss;
}
spawnPointTag = prefab.ConfigElement.GetAttributeString("spawnpointtag", string.Empty);
@@ -143,7 +143,7 @@ namespace Barotrauma
private List<Level.InterestingPosition> GetAvailableSpawnPositions()
{
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p => spawnPosType.HasFlag(p.PositionType));
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p => SpawnPosType.HasFlag(p.PositionType));
var removals = new List<Level.InterestingPosition>();
foreach (var position in availablePositions)
{
@@ -188,8 +188,8 @@ namespace Barotrauma
spawnPos = Vector2.Zero;
var availablePositions = GetAvailableSpawnPositions();
var chosenPosition = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
bool isRuinOrWreck = spawnPosType.HasFlag(Level.PositionType.Ruin) || spawnPosType.HasFlag(Level.PositionType.Wreck);
if (affectSubImmediately && !isRuinOrWreck && !spawnPosType.HasFlag(Level.PositionType.Abyss))
bool isRuinOrWreck = SpawnPosType.HasFlag(Level.PositionType.Ruin) || SpawnPosType.HasFlag(Level.PositionType.Wreck);
if (affectSubImmediately && !isRuinOrWreck && !SpawnPosType.HasFlag(Level.PositionType.Abyss))
{
if (availablePositions.None())
{
@@ -288,11 +288,14 @@ namespace Barotrauma
spawnPos = chosenPosition.Position.ToVector2();
if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
{
var spawnPoint =
WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine, useSyncedRand: false, spawnPointTag: spawnPointTag);
bool ignoreSubmarine = chosenPosition.Ruin != null;
var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, useSyncedRand: false, spawnPointTag: spawnPointTag, ignoreSubmarine: ignoreSubmarine);
if (spawnPoint != null)
{
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == (chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine));
if (!ignoreSubmarine)
{
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
}
spawnPos = spawnPoint.WorldPosition;
}
else
@@ -303,32 +306,42 @@ namespace Barotrauma
return;
}
}
else if ((chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath)
&& offset > 0)
else if (chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath)
{
Vector2 dir;
var waypoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
var nearestWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, spawnPos.Value)).FirstOrDefault();
if (nearestWaypoint != null)
if (offset > 0)
{
int currentIndex = waypoints.IndexOf(nearestWaypoint);
var nextWaypoint = waypoints[Math.Min(currentIndex + 20, waypoints.Count - 1)];
dir = Vector2.Normalize(nextWaypoint.WorldPosition - nearestWaypoint.WorldPosition);
// Ensure that the spawn position is not offset to the left.
if (dir.X < 0)
Vector2 dir;
var waypoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null && wp.Ruin == null);
var nearestWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, spawnPos.Value)).FirstOrDefault();
if (nearestWaypoint != null)
{
dir.X = 0;
int currentIndex = waypoints.IndexOf(nearestWaypoint);
var nextWaypoint = waypoints[Math.Min(currentIndex + 20, waypoints.Count - 1)];
dir = Vector2.Normalize(nextWaypoint.WorldPosition - nearestWaypoint.WorldPosition);
// Ensure that the spawn position is not offset to the left.
if (dir.X < 0)
{
dir.X = 0;
}
}
else
{
dir = new Vector2(1, Rand.Range(-1, 1));
}
Vector2 targetPos = spawnPos.Value + dir * offset;
var targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
if (targetWaypoint != null)
{
spawnPos = targetWaypoint.WorldPosition;
}
}
else
// Ensure that the position is not inside a submarine (in practice wrecks).
if (Submarine.Loaded.Any(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(spawnPos.Value)))
{
dir = new Vector2(1, Rand.Range(-1, 1));
}
Vector2 targetPos = spawnPos.Value + dir * offset;
var targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
if (targetWaypoint != null)
{
spawnPos = targetWaypoint.WorldPosition;
//no suitable position found, disable the event
spawnPos = null;
Finished();
return;
}
}
spawnPending = true;
@@ -371,7 +384,7 @@ namespace Barotrauma
if (spawnPending)
{
//wait until there are no submarines at the spawnpos
if (spawnPosType.HasFlag(Level.PositionType.MainPath) || spawnPosType.HasFlag(Level.PositionType.SidePath) || spawnPosType.HasFlag(Level.PositionType.Abyss))
if (SpawnPosType.HasFlag(Level.PositionType.MainPath) || SpawnPosType.HasFlag(Level.PositionType.SidePath) || SpawnPosType.HasFlag(Level.PositionType.Abyss))
{
foreach (Submarine submarine in Submarine.Loaded)
{
@@ -380,17 +393,29 @@ namespace Barotrauma
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { return; }
}
}
//if spawning in a ruin/cave, wait for someone to be close to it to spawning
//unnecessary monsters in places the players might never visit during the round
if (spawnPosType.HasFlag(Level.PositionType.Ruin) || spawnPosType.HasFlag(Level.PositionType.Cave) || spawnPosType.HasFlag(Level.PositionType.Wreck))
float minDistance = Prefab.SpawnDistance;
if (minDistance <= 0)
{
if (SpawnPosType.HasFlag(Level.PositionType.Cave))
{
minDistance = 8000;
}
else if (SpawnPosType.HasFlag(Level.PositionType.Ruin))
{
minDistance = 5000;
}
else if (SpawnPosType.HasFlag(Level.PositionType.Wreck))
{
minDistance = 3000;
}
}
if (minDistance > 0)
{
bool someoneNearby = false;
float minDist = Sonar.DefaultSonarRange * 0.8f;
foreach (Submarine submarine in Submarine.Loaded)
{
if (submarine.Info.Type != SubmarineType.Player) { continue; }
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist)
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < MathUtils.Pow2(minDistance))
{
someoneNearby = true;
break;
@@ -400,7 +425,7 @@ namespace Barotrauma
{
if (c == Character.Controlled || c.IsRemotePlayer)
{
if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist)
if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < MathUtils.Pow2(minDistance))
{
someoneNearby = true;
break;
@@ -411,7 +436,7 @@ namespace Barotrauma
}
if (spawnPosType.HasFlag(Level.PositionType.Abyss) || spawnPosType.HasFlag(Level.PositionType.AbyssCave))
if (SpawnPosType.HasFlag(Level.PositionType.Abyss) || SpawnPosType.HasFlag(Level.PositionType.AbyssCave))
{
bool anyInAbyss = false;
foreach (Submarine submarine in Submarine.Loaded)
@@ -432,7 +457,7 @@ namespace Barotrauma
int amount = Rand.Range(minAmount, maxAmount + 1);
monsters = new List<Character>();
float scatterAmount = scatter;
if (spawnPosType.HasFlag(Level.PositionType.SidePath))
if (SpawnPosType.HasFlag(Level.PositionType.SidePath))
{
var sidePaths = Level.Loaded.Tunnels.Where(t => t.Type == Level.TunnelType.SidePath);
if (sidePaths.Any())
@@ -444,7 +469,7 @@ namespace Barotrauma
scatterAmount = scatter;
}
}
else if (!spawnPosType.HasFlag(Level.PositionType.MainPath))
else if (!SpawnPosType.HasFlag(Level.PositionType.MainPath))
{
scatterAmount = 0;
}
@@ -474,6 +499,27 @@ namespace Barotrauma
}
Character createdCharacter = Character.Create(speciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true);
var eventManager = GameMain.GameSession.EventManager;
if (eventManager != null)
{
if (SpawnPosType.HasFlag(Level.PositionType.MainPath) || SpawnPosType.HasFlag(Level.PositionType.SidePath))
{
eventManager.CumulativeMonsterStrengthMain += createdCharacter.Params.AI.CombatStrength;
eventManager.AddTimeStamp(this);
}
else if (SpawnPosType.HasFlag(Level.PositionType.Ruin))
{
eventManager.CumulativeMonsterStrengthRuins += createdCharacter.Params.AI.CombatStrength;
}
else if (SpawnPosType.HasFlag(Level.PositionType.Wreck))
{
eventManager.CumulativeMonsterStrengthWrecks += createdCharacter.Params.AI.CombatStrength;
}
else if (SpawnPosType.HasFlag(Level.PositionType.Cave))
{
eventManager.CumulativeMonsterStrengthCaves += createdCharacter.Params.AI.CombatStrength;
}
}
if (GameMain.GameSession.IsCurrentLocationRadiated())
{
AfflictionPrefab radiationPrefab = AfflictionPrefab.RadiationSickness;
@@ -490,6 +536,7 @@ namespace Barotrauma
//this will do nothing if the monsters have no swarm behavior defined,
//otherwise it'll make the spawned characters act as a swarm
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
DebugConsole.NewMessage($"Spawned: {ToString()}. Strength: {StringFormatter.FormatZeroDecimal(monsters.Sum(m => m.Params.AI.CombatStrength))}.", Color.LightBlue, debugOnly: true);
}
}, Rand.Range(0f, amount / 2f));
}