v0.14.6.0
This commit is contained in:
@@ -50,7 +50,7 @@ namespace Barotrauma
|
||||
private float calculateDistanceTraveledTimer;
|
||||
private float distanceTraveled;
|
||||
|
||||
private float avgCrewHealth, avgHullIntegrity, floodingAmount, fireAmount, enemyDanger;
|
||||
private float avgCrewHealth, avgHullIntegrity, floodingAmount, fireAmount, enemyDanger, monsterTotalStrength;
|
||||
|
||||
private float roundDuration;
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Barotrauma
|
||||
totalPathLength = 0.0f;
|
||||
if (level != null)
|
||||
{
|
||||
var steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(Level.Loaded.StartPosition), ConvertUnits.ToSimUnits(Level.Loaded.EndPosition));
|
||||
var steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(level.StartPosition), ConvertUnits.ToSimUnits(level.EndPosition));
|
||||
totalPathLength = steeringPath.TotalLength;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Barotrauma
|
||||
}
|
||||
MTRandom rand = new MTRandom(seed);
|
||||
|
||||
var initialEventSet = SelectRandomEvents(EventSet.List);
|
||||
var initialEventSet = SelectRandomEvents(EventSet.List, rand);
|
||||
if (initialEventSet != null)
|
||||
{
|
||||
pendingEventSets.Add(initialEventSet);
|
||||
@@ -168,7 +168,7 @@ namespace Barotrauma
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.prefab))
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
@@ -374,11 +374,11 @@ namespace Barotrauma
|
||||
preloadedSprites.Clear();
|
||||
}
|
||||
|
||||
private float CalculateCommonness(Pair<EventPrefab, float> eventPrefab)
|
||||
private float CalculateCommonness(EventPrefab eventPrefab, float baseCommonness)
|
||||
{
|
||||
if (level.LevelData.NonRepeatableEvents.Contains(eventPrefab.First)) { return 0.0f; }
|
||||
float retVal = eventPrefab.Second;
|
||||
if (level.LevelData.EventHistory.Contains(eventPrefab.First)) { retVal *= 0.1f; }
|
||||
if (level.LevelData.NonRepeatableEvents.Contains(eventPrefab)) { return 0.0f; }
|
||||
float retVal = baseCommonness;
|
||||
if (level.LevelData.EventHistory.Contains(eventPrefab)) { retVal *= 0.1f; }
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -386,21 +386,25 @@ 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
|
||||
int applyCount = 1;
|
||||
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
|
||||
if (eventSet.PerRuin)
|
||||
{
|
||||
applyCount = Level.Loaded.Ruins.Count();
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
applyCount = level.Ruins.Count();
|
||||
foreach (var ruin in level.Ruins)
|
||||
{
|
||||
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Ruin == ruin; });
|
||||
}
|
||||
}
|
||||
else if (eventSet.PerCave)
|
||||
{
|
||||
applyCount = Level.Loaded.Caves.Count();
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
applyCount = level.Caves.Count();
|
||||
foreach (var cave in level.Caves)
|
||||
{
|
||||
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Cave == cave; });
|
||||
}
|
||||
@@ -416,49 +420,62 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var suitablePrefabs = eventSet.EventPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.First.BiomeIdentifier) ||
|
||||
e.First.BiomeIdentifier.Equals(Level.Loaded.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
string.IsNullOrEmpty(e.prefab.BiomeIdentifier) ||
|
||||
e.prefab.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
if (suitablePrefabs.Count > 0)
|
||||
{
|
||||
List<Pair<EventPrefab, float>> unusedEvents = new List<Pair<EventPrefab, float>>(suitablePrefabs);
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(suitablePrefabs);
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => CalculateCommonness(e) <= 0.0f)) { break; }
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e)).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
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)
|
||||
{
|
||||
var newEvent = eventPrefab.First.CreateInstance();
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
if (i < spawnPosFilter.Count) { newEvent.SpawnPosFilter = spawnPosFilter[i]; }
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"Initialized event {newEvent}");
|
||||
#else
|
||||
DebugConsole.Log($"Initialized event {newEvent}");
|
||||
#endif
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
selectedEvents.Add(eventSet, new List<Event>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
unusedEvents.Remove(eventPrefab);
|
||||
unusedEvents.Remove((eventPrefab, commonness, probability));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eventSet.ChildSets.Count > 0)
|
||||
{
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
|
||||
if (newEventSet != null) { CreateEvents(newEventSet, rand); }
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets, rand);
|
||||
if (newEventSet != null)
|
||||
{
|
||||
CreateEvents(newEventSet, rand);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Pair<EventPrefab, float> eventPrefab in suitablePrefabs)
|
||||
foreach ((EventPrefab eventPrefab, float commonness, float probability) in suitablePrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.First.CreateInstance();
|
||||
if (rand.NextDouble() > probability) { continue; }
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"Initialized event {newEvent}");
|
||||
#else
|
||||
DebugConsole.Log($"Initialized event {newEvent}");
|
||||
#endif
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
selectedEvents.Add(eventSet, new List<Event>());
|
||||
@@ -474,10 +491,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private EventSet SelectRandomEvents(List<EventSet> eventSets)
|
||||
private EventSet SelectRandomEvents(List<EventSet> eventSets, Random random = null)
|
||||
{
|
||||
if (level == null) { return null; }
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
Random rand = random ?? new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
|
||||
var allowedEventSets =
|
||||
eventSets.Where(es =>
|
||||
@@ -496,7 +513,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float totalCommonness = allowedEventSets.Sum(e => e.GetCommonness(level));
|
||||
float randomNumber = (float)rand.NextDouble() * totalCommonness;
|
||||
float randomNumber = (float)rand.NextDouble();
|
||||
randomNumber *= totalCommonness;
|
||||
foreach (EventSet eventSet in allowedEventSets)
|
||||
{
|
||||
float commonness = eventSet.GetCommonness(level);
|
||||
@@ -694,47 +712,103 @@ namespace Barotrauma
|
||||
// enemy amount --------------------------------------------------------
|
||||
|
||||
enemyDanger = 0.0f;
|
||||
monsterTotalStrength = 0;
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead || character.IsIncapacitated || !character.Enabled || character.IsPet || character.Params.CompareGroup("human")) { continue; }
|
||||
if (character.IsIncapacitated || !character.Enabled || character.IsPet || character.Params.CompareGroup("human")) { continue; }
|
||||
|
||||
if (!(character.AIController is EnemyAIController enemyAI)) { continue; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)))
|
||||
{
|
||||
//crawler inside the sub adds 0.1f to enemy danger, mantis 0.25f
|
||||
enemyDanger += enemyAI.CombatStrength / 100.0f;
|
||||
// Enemy onboard -> Crawler inside the sub adds 0.2 to enemy danger, Mudraptor 0.42
|
||||
enemyDanger += enemyAI.CombatStrength / 500.0f;
|
||||
}
|
||||
else if (enemyAI.SelectedAiTarget?.Entity?.Submarine != null)
|
||||
{
|
||||
//enemy outside and targeting the sub or something in it
|
||||
//moloch adds 0.24 to enemy danger, a crawler 0.02
|
||||
enemyDanger += enemyAI.CombatStrength / 1000.0f;
|
||||
// Enemy outside targeting the sub or something in it
|
||||
// -> One Crawler adds 0.02, a Mudraptor 0.042, a Hammerhead 0.1, and a Moloch 0.25.
|
||||
enemyDanger += enemyAI.CombatStrength / 5000.0f;
|
||||
}
|
||||
}
|
||||
// Add a portion of the total strength of active monsters to the enemy danger so that we don't spawn too many monsters around the sub.
|
||||
// On top of the existing value, so if 10 crawlers are targeting the sub simultaneously from outside, the final value would be: 0.02 x 10 + 0.2 = 0.4.
|
||||
// And if they get inside, we add 0.1 per crawler on that.
|
||||
// So, in practice the danger per enemy that is attacking the sub is half of what it would be when the enemy is not targeting the sub.
|
||||
// 10 Crawlers -> +0.2 (0.4 in total if all target the sub from outside).
|
||||
// 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 = 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.
|
||||
// Some examples that result in the max intensity even when the creatures would just idle around.
|
||||
// The values are theoretical, because in practice many of the monsters are targeting the sub, which will double the danger of those monster and effectively halve the max monster count.
|
||||
// In practice we don't use the max intensity. For example on level 50 we use max intensity 50, which would mean that we'd halve the numbers below.
|
||||
// There's no hard cap for the monster count, but if the amount of monsters is higher than this, we don't spawn more monsters from the events:
|
||||
// 50 Crawlers (We shouldn't actually ever spawn that many. 12 is the max per event, but theoretically 25 crawlers would result in max intensity).
|
||||
// 25 Tigerthreshers (Max 9 per event. 12 targeting the sub at the same time results in max intensity).
|
||||
// 10 Hammerheads (Max 3 per event. 5 targeting the sub at the same time results in max intensity).
|
||||
// 4 Molochs (Max 2 per event and 2 targeting the sub at the same time results in max intensity).
|
||||
|
||||
// hull status (gaps, flooding, fire) --------------------------------------------------------
|
||||
|
||||
float holeCount = 0.0f;
|
||||
float waterAmount = 0.0f;
|
||||
float totalHullVolume = 0.0f;
|
||||
float dryHullVolume = 0.0f;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine == null || hull.Submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (hull.RoomName != null && hull.RoomName.Contains("ballast", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (hull.Submarine == null || hull.Submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
if (hull.Submarine.TeamID != CharacterTeamType.Team1 && hull.Submarine.TeamID != CharacterTeamType.Team2) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hull.Submarine.TeamID != CharacterTeamType.Team1) { continue; }
|
||||
}
|
||||
fireAmount += hull.FireSources.Sum(fs => fs.Size.X);
|
||||
if (hull.IsWetRoom) { continue; }
|
||||
foreach (Gap gap in hull.ConnectedGaps)
|
||||
{
|
||||
if (!gap.IsRoomToRoom) holeCount += gap.Open;
|
||||
if (!gap.IsRoomToRoom)
|
||||
{
|
||||
holeCount += gap.Open;
|
||||
}
|
||||
}
|
||||
waterAmount += hull.WaterVolume;
|
||||
totalHullVolume += hull.Volume;
|
||||
fireAmount += hull.FireSources.Sum(fs => fs.Size.X);
|
||||
dryHullVolume += hull.Volume;
|
||||
}
|
||||
if (totalHullVolume > 0)
|
||||
if (dryHullVolume > 0)
|
||||
{
|
||||
floodingAmount = waterAmount / totalHullVolume;
|
||||
floodingAmount = waterAmount / dryHullVolume;
|
||||
}
|
||||
|
||||
//hull integrity at 0.0 if there are 10 or more wide-open holes
|
||||
@@ -779,7 +853,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (level == null) { return 0.0f; }
|
||||
var refEntity = GetRefEntity();
|
||||
Vector2 target = ConvertUnits.ToSimUnits(Level.Loaded.EndPosition);
|
||||
Vector2 target = ConvertUnits.ToSimUnits(level.EndPosition);
|
||||
var steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(refEntity.WorldPosition), target);
|
||||
if (steeringPath.Unreachable || float.IsPositiveInfinity(totalPathLength))
|
||||
{
|
||||
@@ -897,15 +971,15 @@ namespace Barotrauma
|
||||
|
||||
const int maxDist = 1000;
|
||||
|
||||
if (Level.Loaded != null)
|
||||
if (level != null)
|
||||
{
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
foreach (var ruin in level.Ruins)
|
||||
{
|
||||
Rectangle area = ruin.Area;
|
||||
area.Inflate(maxDist, maxDist);
|
||||
if (area.Contains(character.WorldPosition)) { return true; }
|
||||
}
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
foreach (var cave in level.Caves)
|
||||
{
|
||||
Rectangle area = cave.Area;
|
||||
area.Inflate(maxDist, maxDist);
|
||||
|
||||
Reference in New Issue
Block a user