Unstable 0.15.15.0 (and the one before it I forgor)
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user